mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-25 06:01:11 +00:00
fix(amm-client)!: align shared quote wire contract
Use canonical ProgramId word arrays, include pool spot movement in standalone swap quotes, and keep quote commitments stable across deadline refreshes. BREAKING CHANGE: JSON ProgramId values now use eight u32 words. Convert hex or byte representations in host adapters.
This commit is contained in:
@@ -75,9 +75,10 @@ Every call returns an owned JSON envelope. Release it exactly once with `amm_cli
|
||||
[`docs/wire-api.md`](docs/wire-api.md) for the complete transport contract.
|
||||
|
||||
Raw `u128` and `u64` values cross JSON as decimal strings. Account IDs use canonical base58.
|
||||
Program IDs use 64-character lowercase hexadecimal strings. Account data uses hexadecimal, and
|
||||
encoded instruction words remain JSON `u32` numbers. No JavaScript `Number` conversion is required
|
||||
for chain amounts or deadlines. Plan JSON also includes typed `instructionArgs`, derived directly
|
||||
from the same `amm_core::Instruction` encoded in `instructionWords`. Only `amm_client_plan` accepts
|
||||
the five snapshot-bound `prepare_*_transaction` operations. The quote entrypoint also exposes
|
||||
Program IDs use exactly eight JSON `u32` words; hexadecimal and byte layouts are host-adapter-only.
|
||||
Account data uses hexadecimal, and encoded instruction words remain JSON `u32` numbers. No
|
||||
JavaScript `Number` conversion is required for chain amounts or deadlines. Plan JSON also includes
|
||||
typed `instructionArgs`, derived directly from the same
|
||||
`amm_core::Instruction` encoded in `instructionWords`. Only `amm_client_plan` accepts the five
|
||||
snapshot-bound `prepare_*_transaction` operations. The quote entrypoint also exposes
|
||||
`account_snapshot_from_sequencer_response` and `human_price_ratio_to_q64_64` host adapters.
|
||||
|
||||
@@ -14,9 +14,10 @@ Requests may include `"schema":"amm-client.v1"`. Schema-less requests remain acc
|
||||
compatibility. Every successful wire value and every C envelope identifies the response schema.
|
||||
|
||||
All `u128` amounts, reserves, supplies, fees, nonces, and balances are unsigned decimal strings.
|
||||
All `u64` windows and deadlines are also decimal strings. Program IDs are exactly 64 lowercase
|
||||
hexadecimal characters: the 32 bytes formed by concatenating the eight `u32` words in
|
||||
little-endian byte order. Signed ticks are decimal strings. Account IDs use canonical base58.
|
||||
All `u64` windows and deadlines are also decimal strings. Program IDs are JSON arrays containing
|
||||
exactly eight `u32` words in their canonical order. Hexadecimal and byte layouts are host-adapter
|
||||
concerns and are neither accepted nor emitted by this shared wire API. Signed ticks are decimal
|
||||
strings. Account IDs use canonical base58.
|
||||
Account `data` is an even-length hexadecimal string.
|
||||
|
||||
## Shared inputs
|
||||
@@ -25,9 +26,9 @@ Plan context:
|
||||
|
||||
```json
|
||||
{
|
||||
"ammProgramId": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"tokenProgramId": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"twapOracleProgramId": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"ammProgramId": [0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"tokenProgramId": [0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"twapOracleProgramId": [0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"authority": "base58-account-id"
|
||||
}
|
||||
```
|
||||
@@ -54,7 +55,7 @@ Fetched account snapshot used by quotes:
|
||||
```json
|
||||
{
|
||||
"id": "base58-account-id",
|
||||
"programOwner": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"programOwner": [0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"balance": "0",
|
||||
"nonce": "0",
|
||||
"data": "00ff"
|
||||
@@ -65,7 +66,7 @@ Existing-pool quote operations include these top-level state fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"ammProgramId": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"ammProgramId": [0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"config": { "...": "account snapshot" },
|
||||
"snapshot": {
|
||||
"pool": { "...": "account snapshot" },
|
||||
@@ -129,7 +130,7 @@ A successful plan value contains the following fields (`instructionWords` is abb
|
||||
"maxAmountToAddTokenB": "100",
|
||||
"deadline": "1900000000000"
|
||||
},
|
||||
"programId": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"programId": [0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"accounts": [
|
||||
{
|
||||
"id": "base58-account-id",
|
||||
@@ -194,7 +195,8 @@ Quote values use these result shapes:
|
||||
- pool creation: `pool`, `lockedLiquidity`, `userLiquidity`;
|
||||
- add liquidity: `actualAmountA`, `actualAmountB`, `liquidityToMint`, `pool`;
|
||||
- remove liquidity: `withdrawAmountA`, `withdrawAmountB`, `liquidityToBurn`, `pool`;
|
||||
- swaps: `direction`, `amountIn`, `effectiveAmountIn`, `feeAmount`, `amountOut`, `pool`;
|
||||
- swaps: `direction`, `amountIn`, `effectiveAmountIn`, `feeAmount`, `amountOut`, `pool`, and
|
||||
decimal-string `poolSpotChangeBps`;
|
||||
- reserve sync: `donatedAmountA`, `donatedAmountB`, `pool`;
|
||||
- oracle price: `baseAsset`, `quoteAsset`, `initialPriceQ64_64`, `windowDuration`; and
|
||||
- pair order: `order` (`stored` or `reversed`).
|
||||
@@ -202,6 +204,9 @@ Quote values use these result shapes:
|
||||
A `pool` result contains decimal-string `liquidityPoolSupply`, `reserveA`, `reserveB`, and
|
||||
`spotPriceQ64_64` fields.
|
||||
|
||||
`poolSpotChangeBps` is the exact directional movement of the pool's spot price from the pre-swap
|
||||
snapshot to the returned quote. It is not execution-price impact.
|
||||
|
||||
## Host adapters
|
||||
|
||||
`account_snapshot_from_sequencer_response` accepts the original JSON-RPC response as a JSON string,
|
||||
@@ -295,7 +300,8 @@ Successful task output contains:
|
||||
`poolSpotChangeBps` is `null` for non-swap tasks. Add-liquidity funding requirements use the
|
||||
caller caps. Exact-output swap funding uses the plan's slippage-adjusted `maxAmountIn`. Hosts
|
||||
should refresh snapshots, prepare again, compare `quoteCommitment`, and submit only the refreshed
|
||||
plan.
|
||||
plan. A deadline-only refresh keeps the same `quoteCommitment`; the refreshed plan still carries
|
||||
the deadline to submit.
|
||||
|
||||
## Prepared instruction arguments
|
||||
|
||||
|
||||
@@ -29,8 +29,9 @@ char *amm_client_quote(const char *request_json);
|
||||
|
||||
/*
|
||||
* Raw u128, u64, and signed tick values are decimal JSON strings. Program IDs
|
||||
* are 64-character lowercase hexadecimal strings. Instruction words are JSON
|
||||
* u32 arrays. Account IDs use canonical base58 and account data is hexadecimal.
|
||||
* are JSON arrays of eight u32 words. Hexadecimal and byte layouts are host-adapter-only.
|
||||
* Instruction words are JSON u32 arrays. Account IDs use canonical base58 and account data is
|
||||
* hexadecimal.
|
||||
* Requests may carry schema "amm-client.v1";
|
||||
* schema-less legacy requests remain accepted. Responses use
|
||||
* {"schema":"amm-client.v1","ok":true,"value":...} or the same envelope
|
||||
|
||||
@@ -140,7 +140,10 @@ impl WalletPrerequisites {
|
||||
}
|
||||
}
|
||||
|
||||
/// SHA-256 commitment to the typed request, exact plan, and role-tagged account snapshots.
|
||||
/// SHA-256 commitment to quote-relevant intent, guards, account selection, and snapshots.
|
||||
///
|
||||
/// The transaction deadline remains part of the returned plan, but is deliberately not committed:
|
||||
/// hosts may refresh their deadline window without invalidating an otherwise unchanged quote.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct QuoteCommitment([u8; 32]);
|
||||
|
||||
@@ -443,7 +446,7 @@ pub fn prepare_create_pool_transaction(
|
||||
account: "AMM pool",
|
||||
expected: "uninitialized pool lifecycle",
|
||||
}
|
||||
.into())
|
||||
.into());
|
||||
}
|
||||
};
|
||||
let first_definition = missing.first_token_definition();
|
||||
@@ -885,8 +888,7 @@ impl<Q> PreparedTransaction<Q> {
|
||||
unique_account_ids.push(account_id);
|
||||
}
|
||||
|
||||
let instruction_words = plan
|
||||
.instruction_data()
|
||||
plan.instruction_data()
|
||||
.map_err(|_| TransactionError::InstructionEncoding)?;
|
||||
let plan_accounts = plan
|
||||
.accounts()
|
||||
@@ -904,11 +906,10 @@ impl<Q> PreparedTransaction<Q> {
|
||||
operation,
|
||||
intent,
|
||||
program_id: plan.program_id(),
|
||||
instruction_words,
|
||||
guards: CommitmentGuards::from(plan.instruction()),
|
||||
plan_accounts,
|
||||
sources,
|
||||
caller_amounts,
|
||||
deadline,
|
||||
};
|
||||
let words = risc0_zkvm::serde::to_vec(&payload)
|
||||
.map_err(|_| TransactionError::CommitmentEncoding)?;
|
||||
@@ -946,11 +947,138 @@ struct PreparedCommitmentPayload {
|
||||
operation: TransactionOperation,
|
||||
intent: TransactionIntent,
|
||||
program_id: ProgramId,
|
||||
instruction_words: Vec<u32>,
|
||||
guards: CommitmentGuards,
|
||||
plan_accounts: Vec<PlanAccountCommitment>,
|
||||
sources: Vec<SnapshotCommitment>,
|
||||
caller_amounts: CallerAmounts,
|
||||
deadline: u64,
|
||||
}
|
||||
|
||||
/// Instruction fields generated from a quote that affect its economic or account-selection
|
||||
/// meaning. Deadlines are intentionally excluded because they are host transaction policy.
|
||||
#[derive(Serialize)]
|
||||
enum CommitmentGuards {
|
||||
Initialize {
|
||||
token_program_id: ProgramId,
|
||||
twap_oracle_program_id: ProgramId,
|
||||
authority: AccountId,
|
||||
},
|
||||
UpdateConfig {
|
||||
token_program_id: Option<ProgramId>,
|
||||
twap_oracle_program_id: Option<ProgramId>,
|
||||
new_authority: Option<AccountId>,
|
||||
},
|
||||
CreatePriceObservations {
|
||||
window_duration: u64,
|
||||
},
|
||||
CreateOraclePriceAccount {
|
||||
window_duration: u64,
|
||||
},
|
||||
CreatePool {
|
||||
token_a_amount: u128,
|
||||
token_b_amount: u128,
|
||||
fees: u128,
|
||||
},
|
||||
AddLiquidity {
|
||||
min_amount_liquidity: u128,
|
||||
max_amount_to_add_token_a: u128,
|
||||
max_amount_to_add_token_b: u128,
|
||||
},
|
||||
RemoveLiquidity {
|
||||
remove_liquidity_amount: u128,
|
||||
min_amount_to_remove_token_a: u128,
|
||||
min_amount_to_remove_token_b: u128,
|
||||
},
|
||||
SwapExactInput {
|
||||
swap_amount_in: u128,
|
||||
min_amount_out: u128,
|
||||
},
|
||||
SwapExactOutput {
|
||||
exact_amount_out: u128,
|
||||
max_amount_in: u128,
|
||||
},
|
||||
SyncReserves,
|
||||
}
|
||||
|
||||
impl From<&amm_core::Instruction> for CommitmentGuards {
|
||||
fn from(instruction: &amm_core::Instruction) -> Self {
|
||||
match instruction {
|
||||
amm_core::Instruction::Initialize {
|
||||
token_program_id,
|
||||
twap_oracle_program_id,
|
||||
authority,
|
||||
} => Self::Initialize {
|
||||
token_program_id: *token_program_id,
|
||||
twap_oracle_program_id: *twap_oracle_program_id,
|
||||
authority: *authority,
|
||||
},
|
||||
amm_core::Instruction::UpdateConfig {
|
||||
token_program_id,
|
||||
twap_oracle_program_id,
|
||||
new_authority,
|
||||
} => Self::UpdateConfig {
|
||||
token_program_id: *token_program_id,
|
||||
twap_oracle_program_id: *twap_oracle_program_id,
|
||||
new_authority: *new_authority,
|
||||
},
|
||||
amm_core::Instruction::CreatePriceObservations { window_duration } => {
|
||||
Self::CreatePriceObservations {
|
||||
window_duration: *window_duration,
|
||||
}
|
||||
}
|
||||
amm_core::Instruction::CreateOraclePriceAccount { window_duration } => {
|
||||
Self::CreateOraclePriceAccount {
|
||||
window_duration: *window_duration,
|
||||
}
|
||||
}
|
||||
amm_core::Instruction::NewDefinition {
|
||||
token_a_amount,
|
||||
token_b_amount,
|
||||
fees,
|
||||
..
|
||||
} => Self::CreatePool {
|
||||
token_a_amount: *token_a_amount,
|
||||
token_b_amount: *token_b_amount,
|
||||
fees: *fees,
|
||||
},
|
||||
amm_core::Instruction::AddLiquidity {
|
||||
min_amount_liquidity,
|
||||
max_amount_to_add_token_a,
|
||||
max_amount_to_add_token_b,
|
||||
..
|
||||
} => Self::AddLiquidity {
|
||||
min_amount_liquidity: *min_amount_liquidity,
|
||||
max_amount_to_add_token_a: *max_amount_to_add_token_a,
|
||||
max_amount_to_add_token_b: *max_amount_to_add_token_b,
|
||||
},
|
||||
amm_core::Instruction::RemoveLiquidity {
|
||||
remove_liquidity_amount,
|
||||
min_amount_to_remove_token_a,
|
||||
min_amount_to_remove_token_b,
|
||||
..
|
||||
} => Self::RemoveLiquidity {
|
||||
remove_liquidity_amount: *remove_liquidity_amount,
|
||||
min_amount_to_remove_token_a: *min_amount_to_remove_token_a,
|
||||
min_amount_to_remove_token_b: *min_amount_to_remove_token_b,
|
||||
},
|
||||
amm_core::Instruction::SwapExactInput {
|
||||
swap_amount_in,
|
||||
min_amount_out,
|
||||
..
|
||||
} => Self::SwapExactInput {
|
||||
swap_amount_in: *swap_amount_in,
|
||||
min_amount_out: *min_amount_out,
|
||||
},
|
||||
amm_core::Instruction::SwapExactOutput {
|
||||
exact_amount_out,
|
||||
max_amount_in,
|
||||
..
|
||||
} => Self::SwapExactOutput {
|
||||
exact_amount_out: *exact_amount_out,
|
||||
max_amount_in: *max_amount_in,
|
||||
},
|
||||
amm_core::Instruction::SyncReserves => Self::SyncReserves,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
||||
@@ -98,7 +98,7 @@ impl From<SequencerAccountError> for WireError {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Deserialize)]
|
||||
#[serde(try_from = "String")]
|
||||
#[serde(transparent)]
|
||||
struct ProgramIdInput(ProgramId);
|
||||
|
||||
impl From<ProgramIdInput> for ProgramId {
|
||||
@@ -107,14 +107,6 @@ impl From<ProgramIdInput> for ProgramId {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for ProgramIdInput {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
parse_program_id(&value).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "operation", rename_all = "snake_case")]
|
||||
enum PlanRequest {
|
||||
@@ -1648,7 +1640,7 @@ pub fn quote_json(value: Value) -> Result<Value, WireError> {
|
||||
&user_output,
|
||||
decimal_u128(&amount_in, "amountIn")?,
|
||||
)?;
|
||||
Ok(swap_quote_json(quote))
|
||||
swap_quote_with_pool_spot_change_json(snapshot.pool(), quote)
|
||||
}
|
||||
QuoteRequest::PrepareSwapExactInput {
|
||||
state,
|
||||
@@ -1673,7 +1665,7 @@ pub fn quote_json(value: Value) -> Result<Value, WireError> {
|
||||
decimal_u128(&amount_in, "amountIn")?,
|
||||
slippage_tolerance(&slippage_bps)?,
|
||||
)?;
|
||||
Ok(prepared_swap_exact_input_json(prepared))
|
||||
prepared_swap_exact_input_json(snapshot.pool(), prepared)
|
||||
}
|
||||
QuoteRequest::SwapExactInput {
|
||||
state,
|
||||
@@ -1698,7 +1690,7 @@ pub fn quote_json(value: Value) -> Result<Value, WireError> {
|
||||
decimal_u128(&amount_in, "amountIn")?,
|
||||
decimal_u128(&minimum_amount_out, "minimumAmountOut")?,
|
||||
)?;
|
||||
Ok(swap_quote_json(quote))
|
||||
swap_quote_with_pool_spot_change_json(snapshot.pool(), quote)
|
||||
}
|
||||
QuoteRequest::PreviewSwapExactOutput {
|
||||
state,
|
||||
@@ -1721,7 +1713,7 @@ pub fn quote_json(value: Value) -> Result<Value, WireError> {
|
||||
&user_output,
|
||||
decimal_u128(&exact_amount_out, "exactAmountOut")?,
|
||||
)?;
|
||||
Ok(swap_quote_json(quote))
|
||||
swap_quote_with_pool_spot_change_json(snapshot.pool(), quote)
|
||||
}
|
||||
QuoteRequest::PrepareSwapExactOutput {
|
||||
state,
|
||||
@@ -1746,7 +1738,7 @@ pub fn quote_json(value: Value) -> Result<Value, WireError> {
|
||||
decimal_u128(&exact_amount_out, "exactAmountOut")?,
|
||||
slippage_tolerance(&slippage_bps)?,
|
||||
)?;
|
||||
Ok(prepared_swap_exact_output_json(prepared))
|
||||
prepared_swap_exact_output_json(snapshot.pool(), prepared)
|
||||
}
|
||||
QuoteRequest::SwapExactOutput {
|
||||
state,
|
||||
@@ -1771,7 +1763,7 @@ pub fn quote_json(value: Value) -> Result<Value, WireError> {
|
||||
decimal_u128(&exact_amount_out, "exactAmountOut")?,
|
||||
decimal_u128(&maximum_amount_in, "maximumAmountIn")?,
|
||||
)?;
|
||||
Ok(swap_quote_json(quote))
|
||||
swap_quote_with_pool_spot_change_json(snapshot.pool(), quote)
|
||||
}
|
||||
QuoteRequest::SyncReserves { state } => {
|
||||
let (_, snapshot) = state.validate()?;
|
||||
@@ -1818,7 +1810,7 @@ fn transaction_plan_json(plan: &TransactionPlan) -> Result<Value, WireError> {
|
||||
Ok(json!({
|
||||
"instruction": plan.instruction_name(),
|
||||
"instructionArgs": instruction_args_json(plan.instruction()),
|
||||
"programId": program_id_hex(plan.program_id()),
|
||||
"programId": program_id_words(plan.program_id()),
|
||||
"accounts": accounts,
|
||||
"affectedAccountIds": plan
|
||||
.affected_account_ids()
|
||||
@@ -1836,8 +1828,8 @@ fn instruction_args_json(instruction: &Instruction) -> Value {
|
||||
twap_oracle_program_id,
|
||||
authority,
|
||||
} => json!({
|
||||
"tokenProgramId": program_id_hex(*token_program_id),
|
||||
"twapOracleProgramId": program_id_hex(*twap_oracle_program_id),
|
||||
"tokenProgramId": program_id_words(*token_program_id),
|
||||
"twapOracleProgramId": program_id_words(*twap_oracle_program_id),
|
||||
"authority": authority.to_string(),
|
||||
}),
|
||||
Instruction::UpdateConfig {
|
||||
@@ -1845,8 +1837,8 @@ fn instruction_args_json(instruction: &Instruction) -> Value {
|
||||
twap_oracle_program_id,
|
||||
new_authority,
|
||||
} => json!({
|
||||
"tokenProgramId": token_program_id.map(program_id_hex),
|
||||
"twapOracleProgramId": twap_oracle_program_id.map(program_id_hex),
|
||||
"tokenProgramId": token_program_id.map(program_id_words),
|
||||
"twapOracleProgramId": twap_oracle_program_id.map(program_id_words),
|
||||
"newAuthority": new_authority.map(|authority| authority.to_string()),
|
||||
}),
|
||||
Instruction::CreatePriceObservations { window_duration }
|
||||
@@ -2001,7 +1993,7 @@ fn account_snapshot_json(snapshot: &AccountSnapshot) -> Value {
|
||||
let account = snapshot.account();
|
||||
json!({
|
||||
"id": snapshot.account_id().to_string(),
|
||||
"programOwner": program_id_hex(account.program_owner),
|
||||
"programOwner": program_id_words(account.program_owner),
|
||||
"balance": account.balance.to_string(),
|
||||
"nonce": account.nonce.0.to_string(),
|
||||
"data": account
|
||||
@@ -2015,10 +2007,10 @@ fn account_snapshot_json(snapshot: &AccountSnapshot) -> Value {
|
||||
|
||||
fn amm_context_json(context: &AmmContext) -> Value {
|
||||
json!({
|
||||
"ammProgramId": program_id_hex(context.amm_program_id),
|
||||
"ammProgramId": program_id_words(context.amm_program_id),
|
||||
"configId": context.config_id().to_string(),
|
||||
"tokenProgramId": program_id_hex(context.token_program_id()),
|
||||
"twapOracleProgramId": program_id_hex(context.twap_oracle_program_id()),
|
||||
"tokenProgramId": program_id_words(context.token_program_id()),
|
||||
"twapOracleProgramId": program_id_words(context.twap_oracle_program_id()),
|
||||
"authority": context.config.authority.to_string(),
|
||||
})
|
||||
}
|
||||
@@ -2289,24 +2281,49 @@ fn swap_quote_json(quote: SwapQuote) -> Value {
|
||||
})
|
||||
}
|
||||
|
||||
fn prepared_swap_exact_input_json(prepared: PreparedSwapExactInput) -> Value {
|
||||
json!({
|
||||
"quote": swap_quote_json(prepared.quote),
|
||||
fn swap_quote_with_pool_spot_change_json(
|
||||
before: &PoolDefinition,
|
||||
quote: SwapQuote,
|
||||
) -> Result<Value, WireError> {
|
||||
let pool_spot_change_bps = crate::pool_spot_change_bps(before, "e)?;
|
||||
let mut value = swap_quote_json(quote);
|
||||
let Some(object) = value.as_object_mut() else {
|
||||
return Err(WireError::new(
|
||||
"response_serialization_failed",
|
||||
"swap quote response must be a JSON object",
|
||||
));
|
||||
};
|
||||
object.insert(
|
||||
String::from("poolSpotChangeBps"),
|
||||
Value::String(pool_spot_change_bps.to_string()),
|
||||
);
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn prepared_swap_exact_input_json(
|
||||
before: &PoolDefinition,
|
||||
prepared: PreparedSwapExactInput,
|
||||
) -> Result<Value, WireError> {
|
||||
Ok(json!({
|
||||
"quote": swap_quote_with_pool_spot_change_json(before, prepared.quote)?,
|
||||
"instructionArgs": {
|
||||
"swapAmountIn": prepared.swap_amount_in.to_string(),
|
||||
"minAmountOut": prepared.min_amount_out.to_string(),
|
||||
},
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn prepared_swap_exact_output_json(prepared: PreparedSwapExactOutput) -> Value {
|
||||
json!({
|
||||
"quote": swap_quote_json(prepared.quote),
|
||||
fn prepared_swap_exact_output_json(
|
||||
before: &PoolDefinition,
|
||||
prepared: PreparedSwapExactOutput,
|
||||
) -> Result<Value, WireError> {
|
||||
Ok(json!({
|
||||
"quote": swap_quote_with_pool_spot_change_json(before, prepared.quote)?,
|
||||
"instructionArgs": {
|
||||
"exactAmountOut": prepared.exact_amount_out.to_string(),
|
||||
"maxAmountIn": prepared.max_amount_in.to_string(),
|
||||
},
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn sync_reserves_quote_json(quote: SyncReservesQuote) -> Value {
|
||||
@@ -2326,35 +2343,8 @@ fn oracle_price_quote_json(quote: OraclePriceAccountQuote) -> Value {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_program_id(value: &str) -> Result<ProgramId, String> {
|
||||
if value.len() != 64
|
||||
|| !value
|
||||
.bytes()
|
||||
.all(|byte| byte.is_ascii_digit() || matches!(byte, b'a'..=b'f'))
|
||||
{
|
||||
return Err(String::from(
|
||||
"program ID must be exactly 64 lowercase hexadecimal characters",
|
||||
));
|
||||
}
|
||||
|
||||
let bytes = hex_bytes(value, "program ID").map_err(|error| error.to_string())?;
|
||||
let mut program_id = [0_u32; 8];
|
||||
for (word, bytes) in program_id.iter_mut().zip(bytes.chunks_exact(4)) {
|
||||
let mut word_bytes = [0_u8; 4];
|
||||
word_bytes.copy_from_slice(bytes);
|
||||
*word = u32::from_le_bytes(word_bytes);
|
||||
}
|
||||
Ok(program_id)
|
||||
}
|
||||
|
||||
fn program_id_hex(program_id: ProgramId) -> String {
|
||||
let mut output = String::with_capacity(64);
|
||||
for word in program_id {
|
||||
for byte in word.to_le_bytes() {
|
||||
output.push_str(&format!("{byte:02x}"));
|
||||
}
|
||||
}
|
||||
output
|
||||
const fn program_id_words(program_id: ProgramId) -> ProgramId {
|
||||
program_id
|
||||
}
|
||||
|
||||
fn account_id(value: &str, field: &str) -> Result<AccountId, WireError> {
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
use nssa_core::program::ProgramId;
|
||||
|
||||
pub fn program_id_hex(program_id: ProgramId) -> String {
|
||||
let mut output = String::with_capacity(64);
|
||||
for word in program_id {
|
||||
for byte in word.to_le_bytes() {
|
||||
output.push_str(&format!("{byte:02x}"));
|
||||
}
|
||||
}
|
||||
output
|
||||
pub const fn program_id_words(program_id: ProgramId) -> ProgramId {
|
||||
program_id
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use amm_client::{
|
||||
Q64_64_ONE,
|
||||
};
|
||||
use amm_core::canonical_token_pair;
|
||||
use common::program_id_hex;
|
||||
use common::program_id_words;
|
||||
use nssa_core::account::AccountId;
|
||||
use serde_json::json;
|
||||
|
||||
@@ -41,7 +41,7 @@ fn raw_sequencer_response_becomes_lossless_snapshot() {
|
||||
assert_eq!(wire["id"], account_id.to_string());
|
||||
assert_eq!(
|
||||
wire["programOwner"],
|
||||
program_id_hex([1, 2, 3, 4, 5, 6, 7, 8])
|
||||
json!(program_id_words([1, 2, 3, 4, 5, 6, 7, 8]))
|
||||
);
|
||||
assert_eq!(wire["balance"], u128::MAX.to_string());
|
||||
assert_eq!(wire["nonce"], "9007199254740993");
|
||||
|
||||
@@ -12,7 +12,7 @@ use amm_core::{
|
||||
compute_config_pda, compute_liquidity_token_pda, compute_pool_pda, compute_vault_pda,
|
||||
AmmConfig, Instruction, PoolDefinition, FEE_TIER_BPS_30, MINIMUM_LIQUIDITY,
|
||||
};
|
||||
use common::program_id_hex;
|
||||
use common::program_id_words;
|
||||
use nssa_core::{
|
||||
account::{Account, AccountId, Data, Nonce},
|
||||
program::ProgramId,
|
||||
@@ -48,7 +48,7 @@ fn call_json(operation: Operation, request: &Value) -> Value {
|
||||
fn snapshot(id: AccountId, account: &Account) -> Value {
|
||||
json!({
|
||||
"id": id.to_string(),
|
||||
"programOwner": program_id_hex(account.program_owner),
|
||||
"programOwner": program_id_words(account.program_owner),
|
||||
"balance": account.balance.to_string(),
|
||||
"nonce": account.nonce.0.to_string(),
|
||||
"data": hex(account.data.as_ref()),
|
||||
@@ -151,13 +151,14 @@ fn protocol_constants_are_exposed_without_numeric_json_values() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn program_ids_require_canonical_lowercase_hex_strings() {
|
||||
let canonical = program_id_hex([0xabcdef01; 8]);
|
||||
fn program_ids_require_exactly_eight_u32_words() {
|
||||
let canonical = program_id_words([0xabcdef01; 8]);
|
||||
let authority = AccountId::new([44; 32]).to_string();
|
||||
|
||||
for invalid in [
|
||||
json!([1, 1, 1, 1, 1, 1, 1, 1]),
|
||||
json!(canonical.to_uppercase()),
|
||||
json!("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
json!([1, 1, 1, 1, 1, 1, 1]),
|
||||
json!([1, 1, 1, 1, 1, 1, 1, 4_294_967_296_u64]),
|
||||
] {
|
||||
let response = call_json(
|
||||
amm_client_plan,
|
||||
@@ -188,9 +189,9 @@ fn successful_plan_preserves_u64_above_javascript_range_in_guest_words() {
|
||||
&json!({
|
||||
"operation": "create_price_observations",
|
||||
"context": {
|
||||
"ammProgramId": program_id_hex(amm_program_id),
|
||||
"tokenProgramId": program_id_hex(token_program_id),
|
||||
"twapOracleProgramId": program_id_hex(twap_oracle_program_id),
|
||||
"ammProgramId": program_id_words(amm_program_id),
|
||||
"tokenProgramId": program_id_words(token_program_id),
|
||||
"twapOracleProgramId": program_id_words(twap_oracle_program_id),
|
||||
"authority": authority.to_string(),
|
||||
},
|
||||
"poolId": pool_id.to_string(),
|
||||
@@ -205,7 +206,7 @@ fn successful_plan_preserves_u64_above_javascript_range_in_guest_words() {
|
||||
);
|
||||
assert_eq!(
|
||||
response["value"]["programId"],
|
||||
program_id_hex(amm_program_id)
|
||||
json!(program_id_words(amm_program_id))
|
||||
);
|
||||
assert!(response["value"]["accounts"].is_array());
|
||||
let words: Vec<u32> = serde_json::from_value(response["value"]["instructionWords"].clone())
|
||||
@@ -263,7 +264,7 @@ fn successful_quote_preserves_u128_above_javascript_range_as_decimal() {
|
||||
amm_client_quote,
|
||||
&json!({
|
||||
"operation": "create_pool",
|
||||
"ammProgramId": program_id_hex(amm_program_id),
|
||||
"ammProgramId": program_id_words(amm_program_id),
|
||||
"config": snapshot(compute_config_pda(amm_program_id), &config_account),
|
||||
"tokenADefinition": snapshot(token_a_id, &definition("A")),
|
||||
"tokenBDefinition": snapshot(token_b_id, &definition("B")),
|
||||
@@ -289,7 +290,7 @@ fn successful_quote_preserves_u128_above_javascript_range_as_decimal() {
|
||||
amm_client_quote,
|
||||
&json!({
|
||||
"operation": "prepare_create_pool",
|
||||
"ammProgramId": program_id_hex(amm_program_id),
|
||||
"ammProgramId": program_id_words(amm_program_id),
|
||||
"config": snapshot(compute_config_pda(amm_program_id), &config_account),
|
||||
"tokenADefinition": snapshot(token_a_id, &definition("A")),
|
||||
"tokenBDefinition": snapshot(token_b_id, &definition("B")),
|
||||
@@ -342,7 +343,7 @@ fn swap_quote_rejects_unrelated_output_holding() {
|
||||
amm_client_quote,
|
||||
&json!({
|
||||
"operation": "preview_swap_exact_input",
|
||||
"ammProgramId": program_id_hex(amm_program_id),
|
||||
"ammProgramId": program_id_words(amm_program_id),
|
||||
"config": snapshot(
|
||||
compute_config_pda(amm_program_id),
|
||||
&account(amm_program_id, Data::from(&config)),
|
||||
|
||||
@@ -481,7 +481,7 @@ fn exact_output_requires_funding_through_its_maximum_input_guard() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commitment_is_stable_and_changes_with_bound_snapshot_or_deadline() {
|
||||
fn commitment_is_stable_across_deadline_refresh_and_changes_with_bound_snapshot() {
|
||||
let fixture = Fixture::new();
|
||||
let missing = MissingPairFixture::new(&fixture);
|
||||
let fresh_lp = AccountSnapshot::new(AccountId::new([30; 32]), Account::default());
|
||||
@@ -522,10 +522,14 @@ fn commitment_is_stable_and_changes_with_bound_snapshot_or_deadline() {
|
||||
));
|
||||
|
||||
let changed_deadline = prepare(&fixture.caller_first_holding, DEADLINE + 1);
|
||||
assert_ne!(
|
||||
assert_eq!(
|
||||
first.quote_commitment(),
|
||||
changed_deadline.quote_commitment()
|
||||
);
|
||||
assert_ne!(
|
||||
first.plan().instruction_data(),
|
||||
changed_deadline.plan().instruction_data()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -6,7 +6,7 @@ use amm_core::{
|
||||
compute_vault_pda, AmmConfig, PoolDefinition, FEE_TIER_BPS_30, MINIMUM_LIQUIDITY,
|
||||
};
|
||||
use clock_core::{ClockAccountData, CLOCK_01_PROGRAM_ACCOUNT_ID};
|
||||
use common::program_id_hex;
|
||||
use common::program_id_words;
|
||||
use nssa_core::{
|
||||
account::{Account, AccountId, Data, Nonce},
|
||||
program::ProgramId,
|
||||
@@ -111,7 +111,7 @@ impl PairIds {
|
||||
fn inspect_request(&self, snapshots: Value) -> Value {
|
||||
json!({
|
||||
"operation": "inspect_pair",
|
||||
"ammProgramId": program_id_hex(AMM_PROGRAM_ID),
|
||||
"ammProgramId": program_id_words(AMM_PROGRAM_ID),
|
||||
"config": config_snapshot(),
|
||||
"firstTokenDefinitionId": self.first_token_id.to_string(),
|
||||
"secondTokenDefinitionId": self.second_token_id.to_string(),
|
||||
@@ -205,7 +205,7 @@ impl PairIds {
|
||||
fn snapshot(id: AccountId, account: &Account) -> Value {
|
||||
json!({
|
||||
"id": id.to_string(),
|
||||
"programOwner": program_id_hex(account.program_owner),
|
||||
"programOwner": program_id_words(account.program_owner),
|
||||
"balance": account.balance.to_string(),
|
||||
"nonce": account.nonce.0.to_string(),
|
||||
"data": account
|
||||
@@ -232,7 +232,7 @@ fn discovery_operations_return_exact_string_account_ids() {
|
||||
|
||||
let config_id = quote_json(json!({
|
||||
"operation": "derive_config_id",
|
||||
"ammProgramId": program_id_hex(AMM_PROGRAM_ID),
|
||||
"ammProgramId": program_id_words(AMM_PROGRAM_ID),
|
||||
}))
|
||||
.expect("legacy schema-less request remains accepted");
|
||||
assert_eq!(config_id["schema"], WIRE_SCHEMA);
|
||||
@@ -245,19 +245,22 @@ fn discovery_operations_return_exact_string_account_ids() {
|
||||
let inspected = quote_json(json!({
|
||||
"schema": WIRE_SCHEMA,
|
||||
"operation": "inspect_config",
|
||||
"ammProgramId": program_id_hex(AMM_PROGRAM_ID),
|
||||
"ammProgramId": program_id_words(AMM_PROGRAM_ID),
|
||||
"config": config.clone(),
|
||||
}))
|
||||
.expect("config must inspect");
|
||||
assert_eq!(inspected["schema"], WIRE_SCHEMA);
|
||||
assert_eq!(inspected["ammProgramId"], program_id_hex(AMM_PROGRAM_ID));
|
||||
assert_eq!(
|
||||
inspected["ammProgramId"],
|
||||
json!(program_id_words(AMM_PROGRAM_ID))
|
||||
);
|
||||
assert_eq!(
|
||||
inspected["tokenProgramId"],
|
||||
program_id_hex(TOKEN_PROGRAM_ID)
|
||||
json!(program_id_words(TOKEN_PROGRAM_ID))
|
||||
);
|
||||
assert_eq!(
|
||||
inspected["twapOracleProgramId"],
|
||||
program_id_hex(TWAP_ORACLE_PROGRAM_ID)
|
||||
json!(program_id_words(TWAP_ORACLE_PROGRAM_ID))
|
||||
);
|
||||
assert_eq!(inspected["authority"], AccountId::new([9; 32]).to_string());
|
||||
|
||||
@@ -272,7 +275,7 @@ fn discovery_operations_return_exact_string_account_ids() {
|
||||
|
||||
let manifest = quote_json(json!({
|
||||
"operation": "derive_pair_read_manifest",
|
||||
"ammProgramId": program_id_hex(AMM_PROGRAM_ID),
|
||||
"ammProgramId": program_id_words(AMM_PROGRAM_ID),
|
||||
"config": config,
|
||||
"firstTokenDefinitionId": first_token_id.to_string(),
|
||||
"secondTokenDefinitionId": second_token_id.to_string(),
|
||||
|
||||
@@ -5,7 +5,7 @@ use amm_core::{
|
||||
compute_config_pda, compute_liquidity_token_pda, compute_pool_pda, compute_vault_pda,
|
||||
AmmConfig, PoolDefinition, FEE_TIER_BPS_30,
|
||||
};
|
||||
use common::program_id_hex;
|
||||
use common::program_id_words;
|
||||
use nssa_core::{
|
||||
account::{Account, AccountId, Data, Nonce},
|
||||
program::ProgramId,
|
||||
@@ -29,7 +29,7 @@ fn account(program_owner: ProgramId, data: Data) -> Account {
|
||||
fn snapshot(id: AccountId, account: &Account) -> Value {
|
||||
json!({
|
||||
"id": id.to_string(),
|
||||
"programOwner": program_id_hex(account.program_owner),
|
||||
"programOwner": program_id_words(account.program_owner),
|
||||
"balance": account.balance.to_string(),
|
||||
"nonce": account.nonce.0.to_string(),
|
||||
"data": account
|
||||
@@ -102,7 +102,7 @@ impl WireFixture {
|
||||
fees: FEE_TIER_BPS_30,
|
||||
};
|
||||
let state = json!({
|
||||
"ammProgramId": program_id_hex(AMM_PROGRAM_ID),
|
||||
"ammProgramId": program_id_words(AMM_PROGRAM_ID),
|
||||
"config": config,
|
||||
"snapshot": {
|
||||
"pool": snapshot(pool_id, &account(AMM_PROGRAM_ID, Data::from(&pool))),
|
||||
@@ -164,7 +164,7 @@ fn prepare_wire_operations_return_lossless_instruction_args() {
|
||||
|
||||
let create = quote_json(json!({
|
||||
"operation": "prepare_create_pool",
|
||||
"ammProgramId": program_id_hex(AMM_PROGRAM_ID),
|
||||
"ammProgramId": program_id_words(AMM_PROGRAM_ID),
|
||||
"config": fixture.config.clone(),
|
||||
"tokenADefinition": snapshot(fixture.token_a_id, &definition(100_000, None)),
|
||||
"tokenBDefinition": snapshot(fixture.token_b_id, &definition(100_000, None)),
|
||||
@@ -264,6 +264,159 @@ fn prepare_wire_operations_return_lossless_instruction_args() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standalone_swap_quotes_include_exact_pool_spot_change() {
|
||||
let fixture = WireFixture::new();
|
||||
let input_holding = fixture.user_a.clone();
|
||||
let output_holding = fixture.user_b.clone();
|
||||
let input_token_definition_id = fixture.token_a_id.to_string();
|
||||
|
||||
let mut preview_exact_input = fixture.request("preview_swap_exact_input");
|
||||
insert(
|
||||
&mut preview_exact_input,
|
||||
"userInputHolding",
|
||||
input_holding.clone(),
|
||||
);
|
||||
insert(
|
||||
&mut preview_exact_input,
|
||||
"userOutputHolding",
|
||||
output_holding.clone(),
|
||||
);
|
||||
insert(
|
||||
&mut preview_exact_input,
|
||||
"inputTokenDefinitionId",
|
||||
json!(input_token_definition_id.clone()),
|
||||
);
|
||||
insert(&mut preview_exact_input, "amountIn", json!("100"));
|
||||
let preview_exact_input = quote_json(preview_exact_input).expect("preview must quote");
|
||||
|
||||
let mut prepare_exact_input = fixture.request("prepare_swap_exact_input");
|
||||
insert(
|
||||
&mut prepare_exact_input,
|
||||
"userInputHolding",
|
||||
input_holding.clone(),
|
||||
);
|
||||
insert(
|
||||
&mut prepare_exact_input,
|
||||
"userOutputHolding",
|
||||
output_holding.clone(),
|
||||
);
|
||||
insert(
|
||||
&mut prepare_exact_input,
|
||||
"inputTokenDefinitionId",
|
||||
json!(input_token_definition_id.clone()),
|
||||
);
|
||||
insert(&mut prepare_exact_input, "amountIn", json!("100"));
|
||||
insert(&mut prepare_exact_input, "slippageBps", json!("100"));
|
||||
let prepare_exact_input = quote_json(prepare_exact_input).expect("preparation must quote");
|
||||
|
||||
let mut exact_input = fixture.request("swap_exact_input");
|
||||
insert(&mut exact_input, "userInputHolding", input_holding.clone());
|
||||
insert(
|
||||
&mut exact_input,
|
||||
"userOutputHolding",
|
||||
output_holding.clone(),
|
||||
);
|
||||
insert(
|
||||
&mut exact_input,
|
||||
"inputTokenDefinitionId",
|
||||
json!(input_token_definition_id.clone()),
|
||||
);
|
||||
insert(&mut exact_input, "amountIn", json!("100"));
|
||||
insert(&mut exact_input, "minimumAmountOut", json!("1"));
|
||||
let exact_input = quote_json(exact_input).expect("exact-input quote must succeed");
|
||||
|
||||
let mut preview_exact_output = fixture.request("preview_swap_exact_output");
|
||||
insert(
|
||||
&mut preview_exact_output,
|
||||
"userInputHolding",
|
||||
input_holding.clone(),
|
||||
);
|
||||
insert(
|
||||
&mut preview_exact_output,
|
||||
"userOutputHolding",
|
||||
output_holding.clone(),
|
||||
);
|
||||
insert(
|
||||
&mut preview_exact_output,
|
||||
"inputTokenDefinitionId",
|
||||
json!(input_token_definition_id.clone()),
|
||||
);
|
||||
insert(&mut preview_exact_output, "exactAmountOut", json!("45"));
|
||||
let preview_exact_output = quote_json(preview_exact_output).expect("preview must quote");
|
||||
|
||||
let mut prepare_exact_output = fixture.request("prepare_swap_exact_output");
|
||||
insert(
|
||||
&mut prepare_exact_output,
|
||||
"userInputHolding",
|
||||
input_holding.clone(),
|
||||
);
|
||||
insert(
|
||||
&mut prepare_exact_output,
|
||||
"userOutputHolding",
|
||||
output_holding.clone(),
|
||||
);
|
||||
insert(
|
||||
&mut prepare_exact_output,
|
||||
"inputTokenDefinitionId",
|
||||
json!(input_token_definition_id.clone()),
|
||||
);
|
||||
insert(&mut prepare_exact_output, "exactAmountOut", json!("45"));
|
||||
insert(&mut prepare_exact_output, "slippageBps", json!("100"));
|
||||
let prepare_exact_output =
|
||||
quote_json(prepare_exact_output).expect("preparation must quote exact output");
|
||||
|
||||
let mut exact_output = fixture.request("swap_exact_output");
|
||||
insert(&mut exact_output, "userInputHolding", input_holding);
|
||||
insert(&mut exact_output, "userOutputHolding", output_holding);
|
||||
insert(
|
||||
&mut exact_output,
|
||||
"inputTokenDefinitionId",
|
||||
json!(input_token_definition_id),
|
||||
);
|
||||
insert(&mut exact_output, "exactAmountOut", json!("45"));
|
||||
insert(&mut exact_output, "maximumAmountIn", json!("100"));
|
||||
let exact_output = quote_json(exact_output).expect("exact-output quote must succeed");
|
||||
|
||||
let top_level_results = [
|
||||
&preview_exact_input,
|
||||
&exact_input,
|
||||
&preview_exact_output,
|
||||
&exact_output,
|
||||
];
|
||||
for result in top_level_results {
|
||||
assert_eq!(result["poolSpotChangeBps"], "2087");
|
||||
}
|
||||
for result in [&prepare_exact_input, &prepare_exact_output] {
|
||||
assert_eq!(result["quote"]["poolSpotChangeBps"], "2087");
|
||||
}
|
||||
|
||||
let large_amount = 1_u128 << 80;
|
||||
let mut large_input = fixture.request("preview_swap_exact_input");
|
||||
insert(
|
||||
&mut large_input,
|
||||
"userInputHolding",
|
||||
snapshot(
|
||||
AccountId::new([20; 32]),
|
||||
&holding(fixture.token_a_id, large_amount),
|
||||
),
|
||||
);
|
||||
insert(&mut large_input, "userOutputHolding", fixture.user_b);
|
||||
insert(
|
||||
&mut large_input,
|
||||
"inputTokenDefinitionId",
|
||||
json!(fixture.token_a_id.to_string()),
|
||||
);
|
||||
insert(
|
||||
&mut large_input,
|
||||
"amountIn",
|
||||
json!(large_amount.to_string()),
|
||||
);
|
||||
let large_input = quote_json(large_input).expect("large preview must quote");
|
||||
let large_movement = decimal(&large_input["poolSpotChangeBps"]);
|
||||
assert!(large_movement > (1_u128 << 53));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_wire_rejects_out_of_range_slippage() {
|
||||
let fixture = WireFixture::new();
|
||||
|
||||
@@ -6,7 +6,7 @@ use amm_core::{
|
||||
compute_vault_pda, AmmConfig, Instruction, PoolDefinition, FEE_TIER_BPS_30, MINIMUM_LIQUIDITY,
|
||||
};
|
||||
use clock_core::{ClockAccountData, CLOCK_01_PROGRAM_ACCOUNT_ID};
|
||||
use common::program_id_hex;
|
||||
use common::program_id_words;
|
||||
use nssa_core::{
|
||||
account::{Account, AccountId, Data, Nonce},
|
||||
program::ProgramId,
|
||||
@@ -55,7 +55,7 @@ fn holding(definition_id: AccountId, balance: u128) -> Account {
|
||||
fn snapshot(id: AccountId, account: &Account) -> Value {
|
||||
json!({
|
||||
"id": id.to_string(),
|
||||
"programOwner": program_id_hex(account.program_owner),
|
||||
"programOwner": program_id_words(account.program_owner),
|
||||
"balance": account.balance.to_string(),
|
||||
"nonce": account.nonce.0.to_string(),
|
||||
"data": account
|
||||
@@ -212,7 +212,7 @@ impl TransactionFixture {
|
||||
fn active_common(&self, operation: &str) -> Value {
|
||||
json!({
|
||||
"operation": operation,
|
||||
"ammProgramId": program_id_hex(AMM_PROGRAM_ID),
|
||||
"ammProgramId": program_id_words(AMM_PROGRAM_ID),
|
||||
"config": self.config.clone(),
|
||||
"snapshots": self.active_snapshots.clone(),
|
||||
"firstTokenDefinitionId": self.first_token_id.to_string(),
|
||||
@@ -229,7 +229,7 @@ impl TransactionFixture {
|
||||
fn swap_common(&self, operation: &str) -> Value {
|
||||
json!({
|
||||
"operation": operation,
|
||||
"ammProgramId": program_id_hex(AMM_PROGRAM_ID),
|
||||
"ammProgramId": program_id_words(AMM_PROGRAM_ID),
|
||||
"config": self.config.clone(),
|
||||
"snapshots": self.active_snapshots.clone(),
|
||||
"inputTokenDefinitionId": self.first_token_id.to_string(),
|
||||
@@ -326,7 +326,7 @@ fn five_transaction_operations_emit_exact_plans_and_task_artifacts() {
|
||||
let second_amount = LARGE.checked_mul(2).expect("test amount fits");
|
||||
let create = plan_json(json!({
|
||||
"operation": "prepare_create_pool_transaction",
|
||||
"ammProgramId": program_id_hex(AMM_PROGRAM_ID),
|
||||
"ammProgramId": program_id_words(AMM_PROGRAM_ID),
|
||||
"config": fixture.config.clone(),
|
||||
"snapshots": fixture.missing_snapshots.clone(),
|
||||
"firstTokenDefinitionId": fixture.first_token_id.to_string(),
|
||||
|
||||
Reference in New Issue
Block a user