mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-25 22:21:16 +00:00
The swap-submission plan derived the pool's vaults from the canonical token
order (compute_vault_pda(pool, canonical_token_a/b) via derive_pair), but the
guest asserts the provided vaults against the pool's stored vault_a_id /
vault_b_id, which are in the pool's *creation* order. compute_pool_pda_seed
canonicalizes, so the pool address is order-independent, but NewDefinition
stores def_a/def_b (and their vaults) as created — so for a pool created in
non-canonical order, the plan put vault_b in the vault_a slot and the guest
panicked with "Vault A was not provided", reverting the swap.
Read the pool account in swapExactInput and pass its data to
swap_exact_in_plan, which now uses pool.vault_a_id / pool.vault_b_id verbatim
for the vault slots (pool / current_tick / clock stay from derive_pair, since
those are order-independent). This restores the order-agnostic behavior the
original swap client had before it was rewired onto the canonical derive_pair
in 737b2f6.
Adds a regression test that builds a non-canonically-created pool (stored
def_a = the smaller-valued token) and asserts the plan emits the pool's stored
vaults, guarding that they differ from the canonical derivation.
187 lines
5.3 KiB
Rust
187 lines
5.3 KiB
Rust
use serde::Deserialize;
|
|
|
|
use crate::account::AccountRead;
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ConfigIdRequest {
|
|
pub amm_program_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct TokenIdsRequest {
|
|
pub amm_program_id: String,
|
|
pub config: AccountRead,
|
|
#[serde(default)]
|
|
pub wallet_accounts: Vec<AccountRead>,
|
|
#[serde(default)]
|
|
pub configured_token_ids: Vec<String>,
|
|
#[serde(default)]
|
|
pub recent_token_ids: Vec<String>,
|
|
#[serde(default)]
|
|
pub resolved_token_ids: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ContextRequest {
|
|
pub network_id: String,
|
|
pub network_fingerprint: String,
|
|
pub amm_program_id: String,
|
|
pub wallet_available: bool,
|
|
pub config: AccountRead,
|
|
#[serde(default)]
|
|
pub wallet_accounts: Vec<AccountRead>,
|
|
#[serde(default)]
|
|
pub token_definitions: Vec<AccountRead>,
|
|
#[serde(default)]
|
|
pub configured_token_ids: Vec<String>,
|
|
#[serde(default)]
|
|
pub recent_token_ids: Vec<String>,
|
|
#[serde(default)]
|
|
pub resolved_token_ids: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct PairIdsRequest {
|
|
pub amm_program_id: String,
|
|
pub config: AccountRead,
|
|
pub token_a_id: String,
|
|
pub token_b_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct SwapPairRequest {
|
|
pub amm_program_id: String,
|
|
pub token_in_id: String,
|
|
pub token_out_id: String,
|
|
pub config: AccountRead,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ResolvePoolRequest {
|
|
pub pool: AccountRead,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct SwapExactInQuoteRequest {
|
|
pub token_in_id: String,
|
|
pub token_out_id: String,
|
|
pub amount_in_raw: String,
|
|
pub slippage_bps: u32,
|
|
/// Pool account data (hex Borsh `PoolDefinition`). Empty / undecodable ⇒ the
|
|
/// op returns the `no_pool` error.
|
|
pub pool_data: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct SwapExactOutQuoteRequest {
|
|
pub token_in_id: String,
|
|
pub token_out_id: String,
|
|
pub amount_out_raw: String,
|
|
pub slippage_bps: u32,
|
|
/// Pool account data (hex Borsh `PoolDefinition`). Empty / undecodable ⇒ the
|
|
/// op returns the `no_pool` error.
|
|
pub pool_data: String,
|
|
}
|
|
|
|
#[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 SwapExactInPlanRequest {
|
|
pub amm_program_id: String,
|
|
pub token_in_id: String,
|
|
pub token_out_id: String,
|
|
pub config: AccountRead,
|
|
pub user_input_holding_id: String,
|
|
pub user_output_holding_id: String,
|
|
pub amount_in: String,
|
|
pub min_out: String,
|
|
pub deadline_ms: String,
|
|
/// Pool account data (hex Borsh `PoolDefinition`) — its stored `vault_a_id` /
|
|
/// `vault_b_id` are used verbatim (the guest asserts the vaults in the pool's
|
|
/// creation order, which needn't match the canonical token order).
|
|
pub pool_data: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ProgramIdRequest {
|
|
pub elf: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct PositionRequest {
|
|
pub token_a_id: String,
|
|
pub token_b_id: String,
|
|
pub fee_bps: u32,
|
|
#[serde(default)]
|
|
pub amount_a_raw: Option<String>,
|
|
#[serde(default)]
|
|
pub amount_b_raw: Option<String>,
|
|
#[serde(default)]
|
|
pub max_amount_a_raw: Option<String>,
|
|
#[serde(default)]
|
|
pub max_amount_b_raw: Option<String>,
|
|
#[serde(default)]
|
|
pub slippage_bps: Option<u32>,
|
|
#[serde(default)]
|
|
pub initial_price_real_raw: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct PairSnapshot {
|
|
pub config: AccountRead,
|
|
pub token_a: AccountRead,
|
|
pub token_b: AccountRead,
|
|
pub pool: AccountRead,
|
|
pub vault_a: AccountRead,
|
|
pub vault_b: AccountRead,
|
|
pub lp_definition: AccountRead,
|
|
pub lp_lock_holding: AccountRead,
|
|
pub current_tick: AccountRead,
|
|
pub clock: AccountRead,
|
|
pub wallet_available: bool,
|
|
#[serde(default)]
|
|
pub wallet_accounts: Vec<AccountRead>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct QuoteRequest {
|
|
pub network_id: String,
|
|
pub network_fingerprint: String,
|
|
pub amm_program_id: String,
|
|
pub request: PositionRequest,
|
|
pub snapshot: PairSnapshot,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct PlanRequest {
|
|
pub network_id: String,
|
|
pub network_fingerprint: String,
|
|
pub amm_program_id: String,
|
|
pub request: PositionRequest,
|
|
pub snapshot: PairSnapshot,
|
|
pub quote_hash: String,
|
|
pub now_ms: u64,
|
|
#[serde(default)]
|
|
pub fresh_lp: Option<AccountRead>,
|
|
}
|