Files
lez-programs/programs/amm/client/docs/wire-api.md
T

319 lines
14 KiB
Markdown
Raw Normal View History

2026-07-22 13:46:54 -03:00
# 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"}}
2026-07-22 13:46:54 -03:00
```
```json
{"schema":"amm-client.v1","ok":false,"error":{"code":"invalid_request","message":"..."}}
2026-07-22 13:46:54 -03:00
```
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.
2026-07-22 13:46:54 -03:00
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.
2026-07-22 13:46:54 -03:00
## 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" }
}
}
```
2026-07-22 13:46:54 -03:00
## 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 |
2026-07-22 13:46:54 -03:00
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"
},
2026-07-22 13:46:54 -03:00
"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"],
2026-07-22 13:46:54 -03:00
"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.
2026-07-22 13:46:54 -03:00
## 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.
2026-07-22 13:46:54 -03:00
| `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 |
2026-07-22 13:46:54 -03:00
| `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 |
2026-07-22 13:46:54 -03:00
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.
2026-07-22 13:46:54 -03:00
## 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.
2026-07-22 13:46:54 -03:00
## 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.