From f3a14f051ab22402483a12e036050ddbdeda2ac2 Mon Sep 17 00:00:00 2001 From: Andrea Franz Date: Mon, 13 Jul 2026 22:19:46 +0200 Subject: [PATCH] feat(amm): decode_config in amm_client_ffi (reads token/twap program ids from AMM config) --- programs/amm/client-ffi/amm_client_ffi.h | 20 +++++++++++++ programs/amm/client-ffi/src/lib.rs | 36 ++++++++++++++++++++++++ programs/amm/client-ffi/src/pool.rs | 32 ++++++++++++++++++++- 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/programs/amm/client-ffi/amm_client_ffi.h b/programs/amm/client-ffi/amm_client_ffi.h index c7ca8fa..7f4d87c 100644 --- a/programs/amm/client-ffi/amm_client_ffi.h +++ b/programs/amm/client-ffi/amm_client_ffi.h @@ -46,6 +46,16 @@ typedef struct FfiPoolView { bool ok; } FfiPoolView; +/** + * C-ABI mirror of `pool::ConfigView`. + */ +typedef struct FfiConfigView { + uint32_t token_program_id[8]; + uint32_t twap_oracle_program_id[8]; + uint8_t authority[32]; + bool ok; +} FfiConfigView; + /** * Builds the RISC0 instruction words for a `SwapExactInput`. `amount_in` and * `min_out` are little-endian encoded `u128` values (`[u8; 16]`). The @@ -132,4 +142,14 @@ void amm_client_current_tick_pda(const ProgramId *twap, */ bool amm_client_decode_pool(const uint8_t *bytes, uintptr_t len, struct FfiPoolView *out); +/** + * Decodes an `AmmConfig` account's raw bytes into `out`. Returns `false` on + * decode failure, leaving `out` unwritten. + * + * # Safety + * `bytes` must be valid for reads of `len` bytes, and `out` must be a + * valid, non-null pointer to writable memory for a `FfiConfigView`. + */ +bool amm_client_decode_config(const uint8_t *bytes, uintptr_t len, struct FfiConfigView *out); + #endif /* AMM_CLIENT_FFI_H */ diff --git a/programs/amm/client-ffi/src/lib.rs b/programs/amm/client-ffi/src/lib.rs index cec0f4e..bc4e1f8 100644 --- a/programs/amm/client-ffi/src/lib.rs +++ b/programs/amm/client-ffi/src/lib.rs @@ -45,6 +45,15 @@ pub struct FfiPoolView { pub ok: bool, } +/// C-ABI mirror of `pool::ConfigView`. +#[repr(C)] +pub struct FfiConfigView { + pub token_program_id: [u32; 8], + pub twap_oracle_program_id: [u32; 8], + pub authority: [u8; 32], + pub ok: bool, +} + /// # Safety /// `p` must be a valid, non-null pointer to a readable `[u8; 32]`. unsafe fn acc(p: *const [u8; 32]) -> AccountId { @@ -201,3 +210,30 @@ pub unsafe extern "C" fn amm_client_decode_pool( Err(_) => false, } } + +/// Decodes an `AmmConfig` account's raw bytes into `out`. Returns `false` on +/// decode failure, leaving `out` unwritten. +/// +/// # Safety +/// `bytes` must be valid for reads of `len` bytes, and `out` must be a +/// valid, non-null pointer to writable memory for a `FfiConfigView`. +#[no_mangle] +pub unsafe extern "C" fn amm_client_decode_config( + bytes: *const u8, + len: usize, + out: *mut FfiConfigView, +) -> bool { + let b = core::slice::from_raw_parts(bytes, len); + match pool::decode_config(b) { + Ok(v) => { + *out = FfiConfigView { + token_program_id: v.token_program_id, + twap_oracle_program_id: v.twap_oracle_program_id, + authority: v.authority, + ok: true, + }; + true + } + Err(_) => false, + } +} diff --git a/programs/amm/client-ffi/src/pool.rs b/programs/amm/client-ffi/src/pool.rs index a028032..ad54c80 100644 --- a/programs/amm/client-ffi/src/pool.rs +++ b/programs/amm/client-ffi/src/pool.rs @@ -2,7 +2,7 @@ //! can read reserves / fees / vault ids without depending on `amm_core` //! directly. -use amm_core::PoolDefinition; +use amm_core::{AmmConfig, PoolDefinition}; pub struct PoolView { pub def_a: [u8; 32], @@ -31,9 +31,25 @@ pub fn decode_pool(bytes: &[u8]) -> Result { }) } +pub struct ConfigView { + pub token_program_id: [u32; 8], + pub twap_oracle_program_id: [u32; 8], + pub authority: [u8; 32], +} + +pub fn decode_config(bytes: &[u8]) -> Result { + let c: AmmConfig = borsh::from_slice(bytes).map_err(|e| format!("{e:?}"))?; + Ok(ConfigView { + token_program_id: c.token_program_id, + twap_oracle_program_id: c.twap_oracle_program_id, + authority: c.authority.into_value(), + }) +} + #[cfg(test)] mod tests { use amm_core::PoolDefinition; + use nssa_core::account::AccountId; use super::*; @@ -44,4 +60,18 @@ mod tests { let v = decode_pool(&bytes).unwrap(); assert_eq!(v.reserve_a, 0); } + + #[test] + fn decode_config_roundtrip() { + let c = AmmConfig { + token_program_id: [7u32; 8], + twap_oracle_program_id: [9u32; 8], + authority: AccountId::new([0u8; 32]), + }; + let bytes = borsh::to_vec(&c).unwrap(); + let v = decode_config(&bytes).unwrap(); + assert_eq!(v.token_program_id, [7u32; 8]); + assert_eq!(v.twap_oracle_program_id, [9u32; 8]); + assert_eq!(v.authority, [0u8; 32]); + } }