refactor(amm): share quotes and add reusable client APIs - #234
Open
3esmit wants to merge 8 commits into
Open
Conversation
Add validated shared quote orchestration, canonical planners for every guest instruction, and exact RISC Zero serialization. Expose integer-only slippage preparation and lossless JSON/C adapters without runtime deployment identity checks.
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.
Remove the generic sequencer-response adapter and reuse the transaction order mapper. BREAKING CHANGE: account_snapshot_from_sequencer_response and its quote JSON operation are removed. Hosts must normalize RPC responses into canonical account snapshots before calling amm_client.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
amm_program::quoteAPI and make AMM handlers consume its returnedamounts and pool updates.
amm_clientrlib/cdylibwith validated account snapshots and high-levelquote orchestration.
amm_core::Instructionand exact RISC Zero Serde.returning fields that feed the canonical planners directly.
integration.
caller-order preparation, create/add funding validation, and snapshot-to-complete-plan operations
required by the pool-creation and swap consumers.
compatibility enforcement.
Solves #233
Program-owned quote API
The new
amm_program::quotemodule covers:pair_orderswap_directioncreate_poolpreview_add_liquidity,add_liquiditypreview_remove_liquidity,remove_liquiditypreview_swap_exact_input,swap_exact_inputpreview_swap_exact_output,swap_exact_outputsync_reservescreate_oracle_price_accountQuote results expose exact transfer amounts, LP changes, fee-adjusted swap input, fee amount,
post-operation reserves/supply, and Q64.64 spot price where applicable.
These handlers now call the shared functions and consume their results:
new_definition;add_liquidity;remove_liquidity;sync_reserves; andcreate_oracle_price_account.Runtime account validation, signer/init rules, deadlines, post-state ordering, PDA authorization,
and chained token/oracle calls remain in the handlers.
Reusable AMM client
programs/amm/clientis a stateless workspace crate. It performs no RPC, key management, signing,submission, or wallet lifecycle work.
Validated snapshots
The Rust API adds:
AccountSnapshotover canonical NSSAAccountplusAccountId;AmmContext::from_config_account;PoolContext;ValidatedFungibleDefinition;ValidatedFungibleHolding; andValidatedPoolSnapshot.Validation covers config and pool ownership/decoding, canonical pool/vault/LP PDAs, stored account
IDs, configured Token Program ownership, fungible token definitions and holdings, vault definition
IDs, LP supply/self-authority consistency, remove-liquidity LP holdings, opposite swap input/output
tokens, and sufficient swap input balance.
After validation, client quote functions delegate economic calculations directly to
amm_program::quote; the client contains no AMM formula copy.Prepared chain guards
SlippageTolerancevalidates0..=10_000basis points.minimum_guard_amountuses widened integerfloor rounding and keeps positive minimums executable at one raw unit.
maximum_guard_amountuses widened integer ceil rounding and returnsslippage_bound_overflowrather than saturating beyond
u128.The client exposes
prepare_create_pool,prepare_add_liquidity,prepare_remove_liquidity,prepare_swap_exact_input, andprepare_swap_exact_output. Each resultcontains the canonical quote plus the exact instruction amount fields. Adapters choose tolerance but
do not reproduce guard arithmetic. Prepared add-liquidity maxima preserve the caller's original
caps, while the canonical quote returns deterministic actual deposits. This avoids applying the
program's integer-ratio rounding twice for non-divisible reserve ratios. Funding prerequisites use
instruction spend caps for add-liquidity and exact-output swaps, not only current quoted transfers.
Feature-branch completion
Read-only audits of
feat/amm-pool-creationandfeat/amm-swap-onchainidentified the sharedsurface required to delete both local AMM clients. This change completes that surface:
derive_config_id, raw-snapshotinspect_config, shared pre-poolcanonical_pair, andderive_pair_read_manifestso hosts can fetch config, pool, per-token vault, LP definition, LPlock, current-tick, and clock accounts without copying PDA recipes.
inspect_pairwith missing/active status, validated stored/caller order,dependency IDs, stored reserves, vault balances, LP supply, fee tier, and Q64.64 spot price.
an explicit pair against the requested canonical spot price and validate final deposits through
amm_program::quote::create_pool.stored-order instruction values without QML proportional math.
prepare_*_transactionfor create, add, remove, exact-input swap, and exact-output swap.Each raw-snapshot call returns the canonical quote, complete
TransactionPlan, source-bound quotecommitment, writable/affected account IDs, and wallet-owned prerequisites.
pool_spot_change_bps, explicitly named as pool spot-price movement ratherthan execution-price impact.
The host confirmation path re-fetches account state, repeats the same transaction preparation,
compares the quote commitment, and submits only the newly returned plan. App request IDs handle late
async responses separately from the quote commitment.
These additions remain stateless and perform no RPC, wallet account creation, signing, submission,
or runtime release compatibility check.
Transaction planning
The client exposes all ten planners:
plan_initialize;plan_update_config;plan_create_price_observations;plan_create_oracle_price_account;plan_create_poolforNewDefinition;plan_add_liquidity;plan_remove_liquidity;plan_swap_exact_input;plan_swap_exact_output; andplan_sync_reserves.Each
TransactionPlanowns the target program ID, actualamm_core::Instruction, and orderedPlannedAccountrows containing account role, ID, writable, signer, and init flags. Canonical AMM,oracle, and clock helpers derive protocol accounts. Account ID and signer helpers use the same
ordered rows; the completed API also derives writable/affected IDs from them.
Instruction encoding is a direct call over the canonical enum:
No client instruction mirror or alternate codec is introduced.
JSON and C ABI
The client exposes tagged JSON operations through:
plan_jsonfor all ten planners; andquote_jsonfor protocol constants, pair order, pool creation, preview/exact liquidity changes,preview/exact swaps, five prepared high-level operations, reserve sync, and oracle-price
initialization.
The completed API extends those same entry points, without adding C symbols, with discovery,
inspection, opening-liquidity, and five raw-snapshot transaction-preparation operations. Both plan
and quote entry points accept the task operations. Plan values include typed
instructionArgsderived exhaustively from the same
amm_core::Instructionencoded intoinstructionWords.Versioned requests and responses carry an
amm-client.v1schema discriminator. This versions onlythe JSON contract.
All
u128andu64request values use unsigned decimal strings. Quote amounts, reserves, supply,Q64.64 prices, window durations, and raw account balance/nonce values return or travel as decimal
strings. Values above JavaScript's exact integer range never pass through floating point.
The checked-in C header exposes exactly:
Plan and quote calls return owned UTF-8 JSON envelopes.
amm_client_freereleases them and acceptsNULL. Null pointers, invalid UTF-8, malformed JSON, validation failures, serialization failures,and caught panics map to structured boundary errors.
Public errors and API evolution
QuoteErrorCodeis non-exhaustive and owns stable program quote code strings.QuoteErrorexposes typedkind(), stablecode(), and diagnosticmessage().ClientErroris non-exhaustive and covers account identity/owner/data, fungible/token matching,insufficient balance, identical-token pools, slippage bounds, and wrapped quote errors.
IntentErrorcovers opening-price input, checked intent arithmetic, exact price matching, anddirectional pool-price movement.
TransactionErrorwraps validation/intent failures and adds stable fee-mismatch, encoding,quote_changed, and duplicate-account outcomes.WireErrorpreserves stable codes for JSON/C mapping.SUPPORTED_FEE_TIERSis exposed as a slice.Option; existing panic-based helpers remain forcompatibility and call the checked implementations.
Checked amount overflows return structured quote errors. Q64.64 spot-price conversion retains the
canonical saturating behavior at
u128::MAX.Optimistic same-build guarantee
The program and client built from corresponding source share one economic implementation.
The client accepts the supplied
amm_program_idas the target and PDA namespace. Normal protocolvalidation checks fetched accounts and derived addresses relative to that value and configured
Token/TWAP program IDs. The client does not attempt to prove that the supplied program ID is the
correct deployed release.
This change adds no:
Deployment configuration remains responsible for pairing the client and corresponding AMM build.
Feature-branch migration
feat/amm-pool-creationapps/amm/clientafter the completion-gate operations are available.math, proportional add math, quote commitment, and account-plan construction with shared calls.
flags for new-pool vaults or signer/init treatment for an add-liquidity LP destination.
polling, and human formatting in the app.
feat/amm-swap-onchainviews, manual account ordering, and instruction serialization.
token_definition_id_in, the old two-signer layout, oramm_client_program_id_from_elf/AMM_PROGRAM_BIN. Use the configured supplied ProgramId andcurrent one-input-signer shared plan.
wallet's hex/byte representation.
Both hosts need request-generation correlation so late pool/quote replies cannot replace state for a
newer token pair. Signing/submission is single-flight and is not automatically retried after an
ambiguous result; use the returned transaction hash to query status first.
Compatibility
amm_core::Instructionvariant or field-order changes.PoolDefinitionstorage encoding changes.The guest implementation changed. Rebuild the release guest, inspect the resulting ImageID, and
refresh deployment configuration before deployment. This operational identity refresh is not a
runtime client compatibility gate.
Tests
Completed validation:
cargo +nightly fmt --all -- --checktaplo fmt --check .RISC0_SKIP_BUILD=1 cargo +1.94.0 clippy --workspace --all-targets -- -D warningsmake clippy-guestRISC0_DEV_MODE=1 cargo +1.94.0 test -p amm_clientRISC0_DEV_MODE=1 cargo +1.94.0 test --workspace --exclude integration_testsRISC0_DEV_MODE=1 cargo +1.94.0 test -p integration_testsRISC0_SKIP_BUILD=1 cargo +1.94.0 doc -p amm_program -p amm_client --no-depsmake build-programsspel inspect target/guest/amm.binAll commands passed. The release guest build completed for every program, and AMM inspection
returned a valid ProgramId/ImageID. Deployment configuration still owns any identity refresh.
Required coverage includes:
u128values above2^53;0..=10_000basis-point bounds, one-unit minimums, and checked upperoverflow;
u128/u64JSON/C success paths;2^53;affected IDs;
semantics;
NULLfree behavior.