Files
lez-programs/programs/amm/client/docs/wire-api.md
T
Ricardo Guilherme Schmidt a7395aadb7 feat(amm): add reusable client APIs
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.
2026-08-10 11:22:44 -03:00

186 lines
8.0 KiB
Markdown

# AMM client JSON wire API
The C ABI accepts one tagged JSON object and returns one envelope:
```json
{"ok":true,"value":{}}
```
```json
{"ok":false,"error":{"code":"invalid_request","message":"..."}}
```
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. 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" }
}
}
```
## 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` |
A successful plan value contains the following fields (`instructionWords` is abbreviated here):
```json
{
"instruction": "add_liquidity",
"programId": [0, 0, 0, 0, 0, 0, 0, 0],
"accounts": [
{
"id": "base58-account-id",
"role": "config",
"writable": false,
"signer": false,
"init": false
}
],
"instructionWords": [5]
}
```
The real `instructionWords` array contains the complete encoding produced directly from the
canonical `amm_core::Instruction` with RISC Zero Serde. Account rows follow guest/IDL order.
## Quote operations
Send requests to `amm_client_quote` or `wire::quote_json`. Except `protocol_constants`,
`create_pool`, and `prepare_create_pool`, every operation below also includes the existing-pool
quote state described above.
| `operation` | Additional fields |
|---|---|
| `protocol_constants` | none; returns decimal-string `minimumLiquidity`, `feeBpsDenominator`, `slippageBpsDenominator`, and `supportedFeeTiers` |
| `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` |
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.
## 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 are the quote's `actualAmountA` and `actualAmountB`, not the original
possibly lopsided caps. The exact quote is rerun with those fields before they are returned. This
keeps the eventual plan from spending above the displayed/current quoted deposits.
## 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.