mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-25 06:01:11 +00:00
feat(modules/amm): add pool_id operation
Derives a pool's PDA from the AMM program id and the two token ids —
compute_pool_pda(amm_program, canonical(token_in, token_out)) — returning
{ poolId }. Tokens may be given in either order; the op canonicalizes.
Unlike swap_pair, which derives the whole pair account-set (including the
current-tick PDA, which depends on config.twap_oracle_program_id and so
requires reading the config account), the pool address depends only on the
program id and the token pair. So a caller that just needs to locate and
read the pool can skip the config read entirely.
Exposed as the amm_pool_id C ABI export. Additive — no existing op changes.
It backs the config-free pool lookup the upcoming swap-quote path needs (and,
later, resolvePoolAccount).
This commit is contained in:
@@ -30,6 +30,8 @@ char *amm_swap_pair(const char *request_json);
|
||||
|
||||
char *amm_resolve_pool(const char *request_json);
|
||||
|
||||
char *amm_pool_id(const char *request_json);
|
||||
|
||||
char *amm_swap_plan(const char *request_json);
|
||||
|
||||
char *amm_program_id(const char *request_json);
|
||||
|
||||
@@ -21,9 +21,9 @@ mod tests;
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
pub use request::{
|
||||
ConfigIdRequest, ContextRequest, PairIdsRequest, PairSnapshot, PlanRequest, PositionRequest,
|
||||
ProgramIdRequest, QuoteRequest, ResolvePoolRequest, SwapPairRequest, SwapPlanRequest,
|
||||
TokenIdsRequest,
|
||||
ConfigIdRequest, ContextRequest, PairIdsRequest, PairSnapshot, PlanRequest, PoolIdRequest,
|
||||
PositionRequest, ProgramIdRequest, QuoteRequest, ResolvePoolRequest, SwapPairRequest,
|
||||
SwapPlanRequest, TokenIdsRequest,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -103,6 +103,11 @@ pub fn resolve_pool(request: ResolvePoolRequest) -> AmmResult {
|
||||
swap::resolve_pool(request).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Derives the pool PDA for a pair — config-free, so a reader needn't load config.
|
||||
pub fn pool_id(request: PoolIdRequest) -> AmmResult {
|
||||
swap::pool_id(request).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Builds the `SwapExactInput` wallet submission for a token pair.
|
||||
pub fn swap_plan(request: SwapPlanRequest) -> AmmResult {
|
||||
swap::swap_plan(request).map_err(Into::into)
|
||||
|
||||
@@ -67,6 +67,14 @@ pub struct ResolvePoolRequest {
|
||||
pub pool: AccountRead,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PoolIdRequest {
|
||||
pub amm_program_id: String,
|
||||
pub token_in_id: String,
|
||||
pub token_out_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SwapPlanRequest {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
//! new-position ops: pure functions returning JSON `Value`, PDAs reused from
|
||||
//! `pair::derive_pair` so the swap path never re-derives seeds.
|
||||
|
||||
use amm_core::PoolDefinition;
|
||||
use amm_core::{compute_pool_pda, PoolDefinition};
|
||||
use nssa_core::account::AccountId;
|
||||
use risc0_binfmt::ProgramBinary;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use super::{
|
||||
pair::{derive_pair, is_canonical_pair},
|
||||
ProgramIdRequest, ResolvePoolRequest, SwapPairRequest, SwapPlanRequest,
|
||||
PoolIdRequest, ProgramIdRequest, ResolvePoolRequest, SwapPairRequest, SwapPlanRequest,
|
||||
};
|
||||
use crate::account::{
|
||||
account_id_from_hex, account_id_hex, decode_account, parse_program_id, program_id_bytes,
|
||||
@@ -94,6 +94,21 @@ pub(super) fn resolve_pool(request: ResolvePoolRequest) -> Result<Value, String>
|
||||
}))
|
||||
}
|
||||
|
||||
/// Derives the pool PDA for a swap pair (tokens in either order). Config-free —
|
||||
/// the pool address depends only on the AMM program id and the two token ids, so
|
||||
/// a caller that just needs to read the pool doesn't have to load the config
|
||||
/// first (unlike `swap_pair`, which also derives the config-dependent tick PDA).
|
||||
pub(super) fn pool_id(request: PoolIdRequest) -> Result<Value, String> {
|
||||
let amm_program = parse_program_id(&request.amm_program_id)?;
|
||||
let token_in = account_id_from_hex(&request.token_in_id, "token in id")?;
|
||||
let token_out = account_id_from_hex(&request.token_out_id, "token out id")?;
|
||||
if token_in == token_out {
|
||||
return Err(String::from("pool_id requires two distinct tokens"));
|
||||
}
|
||||
let (token_a, token_b) = canonical_pair(token_in, token_out);
|
||||
Ok(json!({ "poolId": account_id_hex(compute_pool_pda(amm_program, token_a, token_b)) }))
|
||||
}
|
||||
|
||||
/// Builds the `SwapExactInput` submission for a pair: the fixed 8-account IDL
|
||||
/// order (vaults canonical, only the user's input holding signs) and the
|
||||
/// instruction words (`risc0_zkvm::serde` — the same encoding the guest
|
||||
@@ -265,4 +280,43 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(plan, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_id_is_order_independent_and_matches_core() {
|
||||
let program = "00".repeat(32);
|
||||
let a = AccountId::new([0xCC; 32]);
|
||||
let b = AccountId::new([0xDD; 32]);
|
||||
|
||||
let ab = pool_id(PoolIdRequest {
|
||||
amm_program_id: program.clone(),
|
||||
token_in_id: account_id_hex(a),
|
||||
token_out_id: account_id_hex(b),
|
||||
})
|
||||
.unwrap();
|
||||
let ba = pool_id(PoolIdRequest {
|
||||
amm_program_id: program.clone(),
|
||||
token_in_id: account_id_hex(b),
|
||||
token_out_id: account_id_hex(a),
|
||||
})
|
||||
.unwrap();
|
||||
// Canonical ordering makes the pool id independent of swap direction.
|
||||
assert_eq!(ab, ba);
|
||||
|
||||
// And it matches amm_core's PDA for the canonical pair.
|
||||
let amm = parse_program_id(&program).unwrap();
|
||||
let (ca, cb) = if is_canonical_pair(a, b) {
|
||||
(a, b)
|
||||
} else {
|
||||
(b, a)
|
||||
};
|
||||
assert_eq!(ab["poolId"], account_id_hex(compute_pool_pda(amm, ca, cb)));
|
||||
|
||||
// Same token in/out is rejected.
|
||||
assert!(pool_id(PoolIdRequest {
|
||||
amm_program_id: program,
|
||||
token_in_id: account_id_hex(a),
|
||||
token_out_id: account_id_hex(a),
|
||||
})
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::api::{
|
||||
self, AmmApiError, AmmResult, ConfigIdRequest, ContextRequest, PairIdsRequest, PlanRequest,
|
||||
ProgramIdRequest, QuoteRequest, ResolvePoolRequest, SwapPairRequest, SwapPlanRequest,
|
||||
TokenIdsRequest,
|
||||
PoolIdRequest, ProgramIdRequest, QuoteRequest, ResolvePoolRequest, SwapPairRequest,
|
||||
SwapPlanRequest, TokenIdsRequest,
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -117,6 +117,11 @@ pub extern "C" fn amm_resolve_pool(request_json: *const c_char) -> *mut c_char {
|
||||
call::<ResolvePoolRequest>(request_json, api::resolve_pool)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn amm_pool_id(request_json: *const c_char) -> *mut c_char {
|
||||
call::<PoolIdRequest>(request_json, api::pool_id)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn amm_swap_plan(request_json: *const c_char) -> *mut c_char {
|
||||
call::<SwapPlanRequest>(request_json, api::swap_plan)
|
||||
|
||||
@@ -6,8 +6,9 @@ mod ffi;
|
||||
pub mod api;
|
||||
|
||||
pub use api::{
|
||||
config_id, context, pair_ids, plan, program_id, quote, resolve_pool, swap_pair, swap_plan,
|
||||
token_ids, AccountRead, AmmApiError, AmmResponse, AmmResult, ConfigIdRequest, ContextRequest,
|
||||
PairIdsRequest, PairSnapshot, PlanRequest, PositionRequest, ProgramIdRequest, QuoteRequest,
|
||||
ResolvePoolRequest, SwapPairRequest, SwapPlanRequest, TokenIdsRequest, WalletAccount,
|
||||
config_id, context, pair_ids, plan, pool_id, program_id, quote, resolve_pool, swap_pair,
|
||||
swap_plan, token_ids, AccountRead, AmmApiError, AmmResponse, AmmResult, ConfigIdRequest,
|
||||
ContextRequest, PairIdsRequest, PairSnapshot, PlanRequest, PoolIdRequest, PositionRequest,
|
||||
ProgramIdRequest, QuoteRequest, ResolvePoolRequest, SwapPairRequest, SwapPlanRequest,
|
||||
TokenIdsRequest, WalletAccount,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user