# AMM client JSON wire API The C ABI accepts one tagged JSON object and returns one envelope: ```json {"schema":"amm-client.v1","ok":true,"value":{"schema":"amm-client.v1"}} ``` ```json {"schema":"amm-client.v1","ok":false,"error":{"code":"invalid_request","message":"..."}} ``` Requests may include `"schema":"amm-client.v1"`. Schema-less requests remain accepted for 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 arrays of eight `u32` words. Signed ticks are decimal strings. Account IDs are base58 strings. Account `data` is an even-length hexadecimal string. ## Shared inputs Plan context: ```json { "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" } ``` Decoded pool input used by existing-pool planners: ```json { "poolId": "base58-account-id", "definitionTokenAId": "base58-account-id", "definitionTokenBId": "base58-account-id", "vaultAId": "base58-account-id", "vaultBId": "base58-account-id", "liquidityPoolId": "base58-account-id", "liquidityPoolSupply": "2000", "reserveA": "1000", "reserveB": "500", "fees": "30" } ``` Fetched account snapshot used by quotes: ```json { "id": "base58-account-id", "programOwner": [0, 0, 0, 0, 0, 0, 0, 0], "balance": "0", "nonce": "0", "data": "00ff" } ``` Existing-pool quote operations include these top-level state fields: ```json { "ammProgramId": [0, 0, 0, 0, 0, 0, 0, 0], "config": { "...": "account snapshot" }, "snapshot": { "pool": { "...": "account snapshot" }, "tokenADefinition": { "...": "account snapshot" }, "tokenBDefinition": { "...": "account snapshot" }, "vaultA": { "...": "account snapshot" }, "vaultB": { "...": "account snapshot" }, "liquidityDefinition": { "...": "account snapshot" } } } ``` Discovery and task-transaction operations use the complete caller-ordered pair read set: ```json { "snapshots": { "pool": { "...": "account snapshot" }, "firstTokenDefinition": { "...": "account snapshot" }, "secondTokenDefinition": { "...": "account snapshot" }, "firstTokenVault": { "...": "account snapshot" }, "secondTokenVault": { "...": "account snapshot" }, "liquidityDefinition": { "...": "account snapshot" }, "lpLockHolding": { "...": "account snapshot" }, "currentTick": { "...": "account snapshot" }, "clock": { "...": "account snapshot" } } } ``` ## Plan operations Send requests to `amm_client_plan` or `wire::plan_json`. | `operation` | Additional fields | |---|---| | `initialize` | `ammProgramId`, `tokenProgramId`, `twapOracleProgramId`, `authority` | | `update_config` | `context`, optional `tokenProgramId`, optional `twapOracleProgramId`, optional `newAuthority` | | `create_price_observations` | `context`, `poolId`, `windowDuration` | | `create_oracle_price_account` | `context`, `poolId`, `windowDuration` | | `create_pool` | `context`, `tokenADefinitionId`, `tokenBDefinitionId`, `userHoldingA`, `userHoldingB`, `userHoldingLp`, `tokenAAmount`, `tokenBAmount`, `fees`, `deadline` | | `add_liquidity` | `context`, `pool`, `userHoldingA`, `userHoldingB`, `userHoldingLp`, `minAmountLiquidity`, `maxAmountToAddTokenA`, `maxAmountToAddTokenB`, `deadline` | | `remove_liquidity` | `context`, `pool`, `userHoldingA`, `userHoldingB`, `userHoldingLp`, `removeLiquidityAmount`, `minAmountToRemoveTokenA`, `minAmountToRemoveTokenB`, `deadline` | | `swap_exact_input` | `context`, `pool`, `userInputHolding`, `userOutputHolding`, `swapAmountIn`, `minAmountOut`, `deadline` | | `swap_exact_output` | `context`, `pool`, `userInputHolding`, `userOutputHolding`, `exactAmountOut`, `maxAmountIn`, `deadline` | | `sync_reserves` | `context`, `pool` | | `prepare_create_pool_transaction` | same task request documented under Task transactions | | `prepare_add_liquidity_transaction` | same task request documented under Task transactions | | `prepare_remove_liquidity_transaction` | same task request documented under Task transactions | | `prepare_swap_exact_input_transaction` | same task request documented under Task transactions | | `prepare_swap_exact_output_transaction` | same task request documented under Task transactions | A successful plan value contains the following fields (`instructionWords` is abbreviated here): ```json { "instruction": "add_liquidity", "instructionArgs": { "minAmountLiquidity": "99", "maxAmountToAddTokenA": "400", "maxAmountToAddTokenB": "100", "deadline": "1900000000000" }, "programId": [0, 0, 0, 0, 0, 0, 0, 0], "accounts": [ { "id": "base58-account-id", "role": "config", "writable": false, "signer": false, "init": false } ], "affectedAccountIds": ["base58-account-id"], "instructionWords": [5] } ``` The real `instructionWords` array contains the complete encoding produced directly from the canonical `amm_core::Instruction` with RISC Zero Serde. `instructionArgs` is exhaustively derived from that same typed instruction, so C++/QML consumers do not decode RISC Zero Serde. Its `u128` and `u64` fields are decimal strings, optional fields are JSON `null`, and account IDs are base58 strings. Account rows follow guest/IDL order. ## Quote operations Send requests to `amm_client_quote` or `wire::quote_json`. Pool economic operations use the existing-pool quote state described above. Discovery, opening intent, and task-transaction operations use the fields shown in this table and the sections below. | `operation` | Additional fields | |---|---| | `protocol_constants` | none; returns decimal-string `minimumLiquidity`, `feeBpsDenominator`, `slippageBpsDenominator`, and `supportedFeeTiers` | | `derive_config_id` | `ammProgramId` | | `inspect_config` | `ammProgramId`, raw `config` snapshot | | `canonical_pair` | `firstTokenDefinitionId`, `secondTokenDefinitionId` | | `derive_pair_read_manifest` | `ammProgramId`, raw `config`, `firstTokenDefinitionId`, `secondTokenDefinitionId` | | `inspect_pair` | fields from `derive_pair_read_manifest` plus complete `snapshots` | | `prepare_minimum_opening_pair` | `desiredPriceQ64_64`, `feeBps` | | `prepare_opening_from_token_a` | `tokenAAmount`, `desiredPriceQ64_64`, `feeBps` | | `prepare_opening_from_token_b` | `tokenBAmount`, `desiredPriceQ64_64`, `feeBps` | | `validate_explicit_opening_pair` | `tokenAAmount`, `tokenBAmount`, `desiredPriceQ64_64`, `feeBps` | | `prepare_caller_opening_pair` | caller token IDs, desired price, fee, and tagged `intent` described below | | `pair_order` | `firstTokenDefinitionId`, `secondTokenDefinitionId` | | `create_pool` | `ammProgramId`, `config`, `tokenADefinition`, `tokenBDefinition`, `tokenAAmount`, `tokenBAmount`, `feeBps` | | `prepare_create_pool` | same fields as `create_pool`; returns quote plus `NewDefinition` instruction arguments | | `preview_add_liquidity` | `maxAmountA`, `maxAmountB` | | `prepare_add_liquidity` | `maxAmountA`, `maxAmountB`, `slippageBps` | | `add_liquidity` | `maxAmountA`, `maxAmountB`, `minimumLiquidity` | | `preview_remove_liquidity` | `userLiquidityHolding`, `removeLiquidityAmount` | | `prepare_remove_liquidity` | `userLiquidityHolding`, `removeLiquidityAmount`, `slippageBps` | | `remove_liquidity` | `userLiquidityHolding`, `removeLiquidityAmount`, `minimumAmountA`, `minimumAmountB` | | `preview_swap_exact_input` | `userInputHolding`, `userOutputHolding`, `inputTokenDefinitionId`, `amountIn` | | `prepare_swap_exact_input` | `userInputHolding`, `userOutputHolding`, `inputTokenDefinitionId`, `amountIn`, `slippageBps` | | `swap_exact_input` | `userInputHolding`, `userOutputHolding`, `inputTokenDefinitionId`, `amountIn`, `minimumAmountOut` | | `preview_swap_exact_output` | `userInputHolding`, `userOutputHolding`, `inputTokenDefinitionId`, `exactAmountOut` | | `prepare_swap_exact_output` | `userInputHolding`, `userOutputHolding`, `inputTokenDefinitionId`, `exactAmountOut`, `slippageBps` | | `swap_exact_output` | `userInputHolding`, `userOutputHolding`, `inputTokenDefinitionId`, `exactAmountOut`, `maximumAmountIn` | | `sync_reserves` | no additional fields | | `create_oracle_price_account` | `windowDuration` | | `prepare_create_pool_transaction` | task-transaction fields below | | `prepare_add_liquidity_transaction` | task-transaction fields below | | `prepare_remove_liquidity_transaction` | task-transaction fields below | | `prepare_swap_exact_input_transaction` | task-transaction fields below | | `prepare_swap_exact_output_transaction` | task-transaction fields below | 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`; - reserve sync: `donatedAmountA`, `donatedAmountB`, `pool`; - oracle price: `baseAsset`, `quoteAsset`, `initialPriceQ64_64`, `windowDuration`; and - pair order: `order` (`stored` or `reversed`). A `pool` result contains decimal-string `liquidityPoolSupply`, `reserveA`, `reserveB`, and `spotPriceQ64_64` fields. ## Discovery, inspection, and opening intents Discovery functions derive IDs only; adapters fetch the returned accounts and submit raw snapshots for inspection. `inspect_pair` returns `status` as `missing` or `active`. Missing output contains the read manifest, caller-ordered definitions, vault lifecycle states, and clock. Active output contains the manifest, `callerOrder`, stored token/vault/LP IDs, reserves, vault balances, LP supply, fee, stored Q64.64 spot price, current tick, and clock. Numeric protocol fields remain strings. `prepare_caller_opening_pair` accepts caller token order without reproducing canonical ordering: ```json { "operation": "prepare_caller_opening_pair", "firstTokenDefinitionId": "base58-account-id", "secondTokenDefinitionId": "base58-account-id", "desiredPriceQ64_64": "18446744073709551616", "feeBps": "30", "intent": { "kind": "first_amount", "amount": "2000" } } ``` Other intent shapes are `{ "kind":"minimum" }`, `{ "kind":"second_amount", "amount":"..." }`, and `{ "kind":"explicit", "firstAmount":"...", "secondAmount":"..." }`. The result includes `callerOrder`, caller `firstAmount`/`secondAmount`, and the canonical stored opening quote and amounts. ## Task transactions The five snapshot-bound task operations are accepted by both `amm_client_plan`/`wire::plan_json` and `amm_client_quote`/`wire::quote_json`. Every request includes `ammProgramId`, raw `config`, the complete caller-ordered `snapshots`, and decimal-string `deadline`. | `operation` | Additional fields | |---|---| | `prepare_create_pool_transaction` | caller token IDs, `firstTokenHolding`, `secondTokenHolding`, `liquidityHolding`, `firstAmount`, `secondAmount`, `feeBps` | | `prepare_add_liquidity_transaction` | caller token IDs and holdings, `maxFirstAmount`, `maxSecondAmount`, `slippageBps`, optional `expectedFeeBps` | | `prepare_remove_liquidity_transaction` | caller token IDs and holdings, `removeLiquidityAmount`, `slippageBps`, optional `expectedFeeBps` | | `prepare_swap_exact_input_transaction` | input/output token IDs and holdings, `amountIn`, `slippageBps`, optional `expectedFeeBps` | | `prepare_swap_exact_output_transaction` | input/output token IDs and holdings, `exactAmountOut`, `slippageBps`, optional `expectedFeeBps` | Successful task output contains: ```json { "operation": "swap_exact_output", "quote": {}, "callerAmounts": { "first": "101", "second": "100" }, "plan": { "instruction": "swap_exact_output", "instructionArgs": { "exactAmountOut": "100", "maxAmountIn": "102", "deadline": "1900000000000" }, "instructionWords": [] }, "quoteCommitment": "64-lowercase-hex-characters", "affectedAccountIds": ["base58-account-id"], "walletPrerequisites": { "signerAccountIds": ["base58-account-id"], "freshAccountIds": [], "funding": [{ "holdingAccountId": "base58-account-id", "tokenDefinitionId": "base58-account-id", "available": "1000", "required": "102" }] }, "deadline": "1900000000000", "poolSpotChangeBps": "42" } ``` `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. ## Prepared instruction arguments The five `prepare_*` operations return the economic result under `quote` and decimal-string chain arguments under `instructionArgs`. Those fields map directly to the matching plan operation: - `prepare_create_pool`: `tokenAAmount`, `tokenBAmount`, `fees`; - `prepare_add_liquidity`: `minAmountLiquidity`, `maxAmountToAddTokenA`, `maxAmountToAddTokenB`; - `prepare_remove_liquidity`: `removeLiquidityAmount`, `minAmountToRemoveTokenA`, `minAmountToRemoveTokenB`; - `prepare_swap_exact_input`: `swapAmountIn`, `minAmountOut`; and - `prepare_swap_exact_output`: `exactAmountOut`, `maxAmountIn`. `slippageBps` accepts `0` through `slippageBpsDenominator` (`10,000`) as an unsigned decimal string. Minimum guards use integer floor rounding and stay at least one raw unit for positive quotes. Maximum guards use integer ceil rounding. A maximum above `u128` returns `slippage_bound_overflow`; an out-of-range tolerance returns `slippage_tolerance_out_of_range`. This calculation runs only in the Rust client, never in JavaScript or QML. Prepared add-liquidity maximums preserve the original caller caps. Replacing them with rounded `actualAmountA` and `actualAmountB` can change the program quote when reserve ratios are not divisible, because execution performs proportional integer rounding again. Funding prerequisites therefore cover the caller caps while display amounts remain the canonical quote's actual deposit. ## Ownership and failures The client validates account decoding, configured owners, canonical PDAs, pool/vault/token/LP relationships, swap input/output pairing, and required input balances. Quote arithmetic failures retain the stable `amm_program::quote::QuoteError` code. No request performs network I/O or checks an ImageID, release version, compatibility manifest, or program allowlist. Deployment configuration is expected to select the corresponding AMM build.