Integrate
no SDK2 enginesRPC-onlyWire both iso engines from any aggregator (cq1, cq2, cqV, dflow, titan, jupiter, etc). All ix bodies are little-endian; only PDA index seeds are big-endian. Decode pool, quote, build ix.
skill.md for AI agentscopy + paste
One copyable, self-contained spec. Paste it into Claude Code, Cursor, or any coding agent to wire iso-xmm with no SDK. Sourced 1:1 from the live TS client + IDL.
# iso-xmm integration skill (paste into any AI coding agent)
A self-contained spec to integrate iso-xmm, a Solana AMM with two live engines (Apex
CLMM + Pulse oracle-anchored maker). No SDK needed: decode the pool, quote, build the
1-byte-discriminator swap instruction, sign, send. Sourced from the live TS client and IDL.
Conventions, fixed:
- Each iso account carries a 1-BYTE discriminator at byte 0 (not Anchor's 8-byte hash).
- Instruction data: byte 0 is the 1-byte ix discriminator; all body scalars LITTLE-endian.
- PDA seeds: every index-bearing seed is BIG-endian (i32/u16 to_be_bytes); the pool
nonce tail is u32 LITTLE-endian. Account-DATA scalars are LITTLE-endian.
- u128 in account data = two LE u64 halves, low 8 bytes then high 8 bytes.
## Address book (mainnet)
program isogrRJ6sEiktnfUY7QdZhDr5Sut3C8ER9HejjUqSZK
amm_config Eousgm9tP3EjdheeB3MKfFU4zvZtxD6cnpY8FijbkG4M
Apex pool isoJANGQx1GmbmLUbGDa34Qxe6GLppFNxrZw5Kx5fX9 (CLMM, SOL/USDC, account disc 2)
Pulse pool isoLWsMtAQT58VzufDJEiXGFV6PjiamtLwseHe6hBVw (anchored, SOL/SPDR, account disc 7)
mpl-core CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d (position NFT)
SPL Token TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Token-2022 TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
IDL (Codama, 1-byte discriminators): https://xmm.mbco.io/xmm.json
Mints: SOL So11111111111111111111111111111111111111112 (9), USDC
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v (6), SPDR
AT79ReYU9XtHUTF5vM6Q4oa9K8w7918Fp5SU7G1MDMQY (9).
## Apex (CLMM) swap, ix disc 6
data (34 bytes):
[0] u8 disc = 6
[1..9] u64 amount (LE)
[9..17] u64 other_amount_threshold (LE) // exact-in -> min out
[17..33] u128 sqrt_price_limit_x64 (LE) // 0 = no limit
[33] u8 is_base_input (1/0)
accounts (10 head, then ordered tail):
0 payer signer, writable
1 amm_config readonly
2 pool writable
3 user_in (ATA) writable
4 user_out (ATA) writable
5 input_vault writable
6 output_vault writable
7 observation writable
8 input_token_program readonly // must own the INPUT vault (SPL vs Token-2022)
9 output_token_program readonly // must own the OUTPUT vault
-- tail --
10 bitmap_ext writable // FIRST in tail
11+ tick_array[] writable // direction-ordered window
Direction is inferred on-chain from the input vault. zero_for_one = input is mint_0
(price falls); one_for_zero = input is mint_1 (price rises). Vaults: zero_for_one ->
input=vault0, output=vault1; else swapped. Tick-array window: start at the array holding
tick_current, walk the swap direction (DOWN for zero_for_one, UP otherwise), step =
tick_spacing * 60; include only on-chain-initialized neighbors (account disc 3), pad to
>=2 with the current array (an uninitialized neighbor reverts 6002). Cap 12 arrays.
## Pulse (anchored) swap, ix disc 18 (swap_anchor)
data (17 bytes):
[0] u8 disc = 18
[1..9] u64 amount (LE)
[9..17] u64 other_amount_threshold (LE) // min out
accounts (8, no tail):
0 payer signer, writable
1 pool readonly
2 user_in (ATA) writable
3 user_out (ATA) writable
4 input_vault writable
5 output_vault writable
6 input_token_program readonly
7 output_token_program readonly
No tick arrays, no bitmap, no observation: the maker posts the mid; fill is at
posted-mid +/- band, zero curve slippage.
## PDA seeds (program id isogrRJ6sEiktnfUY7QdZhDr5Sut3C8ER9HejjUqSZK)
amm_config "amm_config", index index = u16 BE
pool (Apex) "pool", amm_config, mint0, mint1, nonce nonce = u32 LE
pool (Pulse) "pool_anchor", amm_config, mint0, mint1, nonce nonce = u32 LE
vault "pool_vault", pool, mint none
observation "observation", pool none
bitmap_ext "pool_tick_array_bitmap_extension", pool none
tick_array "tick_array", pool, start_tick_index start = i32 BE
position "position", core_asset none (NFT mint)
mint_0 addr < mint_1 addr. tick_array start = floor-toward-negative-infinity of
tick_current over (tick_spacing * 60) ticks/array. ATA = [owner, tokenProgram, mint].
## Pool decode offsets
PoolClmm (disc 2, 1545 B):
2 amm_config pubkey | 66 token_mint0 | 98 token_mint1 | 130 token_vault0 |
162 token_vault1 | 194 observation | 226 mint_decimals0 u8 | 227 mint_decimals1 u8 |
228 tick_spacing u16 | 230 liquidity u128 | 246 sqrt_price_x64 u128 | 262 tick_current i32 |
382 status u8 (bit0=halt) | 1089 fee_rate u32 (bp = fee_rate/100000; 10000 -> 0.1 bp) |
1093 pool_nonce u32
PoolAnchor (disc 7):
72 token_mint0 | 104 token_mint1 | 136 token_vault0 | 168 token_vault1 |
200 pricer_hot_key | 232 mint_decimals0 u8 | 233 mint_decimals1 u8 | 234 status u8 |
235 haircut_bps_p1 u8 | 236 round_mode u8 | 240 price_x64 u128 | 256 bid_factor u64 |
264 ask_factor u64 | 272 confidence u64 | 280 price_updated_ms u64 | 304 capacity_cap u64
## Quote
Apex (CLMM) spot:
price = (sqrt_price_x64 / 2^64)^2 * 10^(dec0 - dec1) // quote per base, human
feeFactor = 1 - fee_rate / 1e9
zeroForOne = inputMint == token_mint0
out = zeroForOne ? amountIn * price * feeFactor : (amountIn / price) * feeFactor
minOut = floor(out * (1 - slippage))
// constant-price at current sqrt_price; walk tick arrays for size. tick->price = 1.0001^tick * 10^(dec0-dec1)
Pulse (anchored): // exact integer replay; see lib/xmm/router.ts anchorQuoteOut
inputIsBase = inputMint == token_mint0
band = (inputIsBase ? bid_factor : ask_factor) || 1e12 // maker side, 1e12-scaled (0=flat)
eff = price_x64 * band / 1e12 // Q64.64 effective price
haircut_bps = data[235]==0 ? 5 : min(data[235]-1, 5) // SENTINEL @235
// round_mode SENTINEL @236: 1 = PRECISE (fold the 10^|dec| rescale into ONE floor-divide,
// no up-rescale truncation); else LEGACY (floor the price divide, THEN rescale). isoZbS=1.
PRECISE base->quote: out = (amountIn*eff*10^max(d1-d0,0)) / (2^64 *10^max(d0-d1,0)) // floor once
PRECISE quote->base: out = (amountIn*2^64*10^max(d0-d1,0)) / (eff *10^max(d1-d0,0)) // floor once
out = floor(out) * (10000 - haircut_bps) / 10000 // quote 0 if halted (status bit0) or stale
## Enumerate / stream
memcmp the 1-byte disc at offset 0. Single-byte disc base58 = one char: disc 2 -> "3",
disc 7 -> "8", position disc 4 -> "5".
getProgramAccounts(programId, { filters: [{ memcmp: { offset: 0, bytes: "3" } }] }) // every Apex pool
getAccountInfo(pool, "base64"); require owner == programId && data[0] == 2 (or 7)
Yellowstone gRPC: subscribe accounts owned by the program; add { memcmp: { offset: "0",
base58: "3" } } to scope one engine. Decode bytes the same way as getAccountInfo.
## Build + send
1. getAccountInfo(pool); require owner == program and data[0] == 2 (Apex) or 7 (Pulse).
2. Decode at the offsets above; quote with the engine formula.
3. Resolve user_in/user_out ATAs; resolve each token program from the mint owner (both
legs SPL for the live pools). SOL leg: wrap via System transfer + SPL sync_native
(byte 17); unwrap after via SPL close_account (byte 9).
4. Build swap ix: data = [disc] + LE body; accounts in the order above. Apex tail =
bitmap_ext FIRST, then the direction-ordered tick-array window.
5. Sign + sendTransaction. Reference CU: Apex ~27,959; Pulse ~10,000.
Address book (mainnet)
Lightcore runtime, 1-byte discriminator dispatch. First-party on-chain TWAP oracle, no external feed.
| What | Address | Note |
|---|---|---|
| program | isogrR..qSZK | owner of all iso accounts |
| amm_config | Eousgm..kG4M | fee tier, shared by both engines |
| Apex SOL/USDC | isoJAN..5fX9 | CLMM, disc 2 |
| Pulse SOL/SPDR | isoLWs..hBVw | anchored, disc 7 |
| mpl-core | CoREEN..hX7d | position NFT (range + fees) |
Engine matrix
Two live engines. Pick by the pool account discriminator at byte 0.
| Field | Apex (CLMM) | Pulse (anchored) |
|---|---|---|
| model | concentrated liquidity | oracle-anchored maker, zero curve slippage |
| pool disc | 2 | 7 |
| swap ix disc | 6 · swap | 18 · swap |
| ix body | u64 amount, u64 thr, u128 sqrtLimit, u8 baseIn (34B) | u64 amount, u64 thr (17B) |
| accounts | 10 head + bitmap + tick arrays | 8 |
| quote | (sqrtP/2^64)^2 * 10^(d0-d1) | priceX64/2^64, then ± band |
| fee | 0.1 bp | zero fee, maker earns the spread |
| swap CU | ~27,959 | ~10,000 |
Reference: closed prop AMMs are ~95,000 CU for a swap. open_position 44,229 / add_liquidity 25,920 / create_pool 38,949 / post_price 680 CU.
Swap instruction layout
data[0] = 1-byte disc. Body scalars LITTLE-endian. inputTokenProgram / outputTokenProgram must match the vault owners (SPL vs Token-2022).
data (34 bytes) [0] u8 disc = 6 [1..9] u64 amount (LE) [9..17] u64 otherAmtThreshold (LE) // min out / max in [17..33] u128 sqrtPriceLimitX64 (LE) // 0 = no limit [33] u8 isBaseInput (1/0) accounts (10 head, then tail) 0 payer signer, w 1 amm_config ro 2 pool w 3 user_in (ATA) w 4 user_out (ATA) w 5 input_vault w 6 output_vault w 7 observation w 8 input_token_prog ro // routes input vault 9 output_token_prog ro // routes output vault -- tail -- 10 bitmap_ext w // FIRST in tail 11.. tick_array[] w // window, ordered
data (17 bytes) [0] u8 disc = 18 [1..9] u64 amount (LE) [9..17] u64 otherAmtThreshold (LE) // min out / max in accounts (8) 0 payer signer, w 1 pool ro 2 user_in (ATA) w 3 user_out (ATA) w 4 input_vault w 5 output_vault w 6 input_token_prog ro // routes input vault 7 output_token_prog ro // routes output vault // no tick arrays, no bitmap, no observation: // the maker posts the mid, fill is at posted-mid ± band.
Direction is inferred on-chain from the input vault. zero_for_one = input is mint_0 (price falls); one_for_zero = input is mint_1 (price rises). The Apex tick-array window starts at tick_current's array and walks the swap direction (down for zero_for_one), max 12 arrays.
PDA derivation
Every index-bearing seed is BIG-endian (to_be_bytes); the pool nonce tail is u32 LITTLE-endian. Account-data scalars are LE.
| Account | Seeds | Encoding |
|---|---|---|
| amm_config | "amm_config", index | index = u16 BE |
| pool (Apex) | "pool", amm_config, mint0, mint1, nonce | nonce = u32 LE |
| pool (Pulse) | "pool_anchor", amm_config, mint0, mint1, nonce | nonce = u32 LE |
| vault | "pool_vault", pool, mint | none |
| observation | "observation", pool | none |
| bitmap_ext | "pool_tick_array_bitmap_extension", pool | none |
| tick_array | "tick_array", pool, start_tick_index | start = i32 BE |
| position | "position", core_asset | none (NFT mint) |
mint_0 addr < mint_1 addr. tick_array start = floor-toward-negative-infinity over tick_spacing × 60 ticks per array.
Pool decode offsets
Fixed-offset decoders. u128 is two LE u64 halves (lo then hi). disc at byte 0.
PoolClmm (disc 2) 2 amm_config pubkey 66 token_mint0 pubkey 98 token_mint1 pubkey 130 token_vault0 pubkey 162 token_vault1 pubkey 194 observation pubkey 226 mint_decimals0 u8 227 mint_decimals1 u8 228 tick_spacing u16 230 liquidity u128 246 sqrt_price_x64 u128 262 tick_current i32 382 status u8 (bit0 = halt) 1089 fee_rate u32 (/1e5 = bp) 1093 pool_nonce u32
PoolAnchor (disc 7) 72 token_mint0 pubkey 104 token_mint1 pubkey 136 token_vault0 pubkey 168 token_vault1 pubkey 200 pricer_hot_key pubkey 232 mint_decimals0 u8 233 mint_decimals1 u8 234 status u8 235 haircut_bps_p1 u8 (SENTINEL: 0=>5bp; v>=1=>min(v-1,5) bp) 236 round_mode u8 (SENTINEL: 1=precise rounding, else legacy) 240 price_x64 u128 256 bid_factor u64 264 ask_factor u64 (band = (f/1e12-1)*1e4 bp) 272 confidence u64 280 price_updated_ms u64 304 capacity_cap u64
Integrate a swap in 3 steps · Apex
Apex (CLMM): walk ticks at the current sqrt_price.
1. DECODE POOL
getAccountInfo(pool); require owner == program && data[0] == 2
read sqrt_price_x64@246, tick_current@262, tick_spacing@228,
decimals@226/227, fee_rate@1089, vaults@130/162
2. QUOTE (spot, refine by walking ticks for size)
price = (sqrt_price_x64 / 2^64)^2 * 10^(dec0 - dec1) // quote per base
feeFactor = 1 - fee_rate / 1e9
zeroForOne = inputMint == token_mint0
out = zeroForOne ? amountIn * price * feeFactor
: (amountIn / price) * feeFactor
minOut = floor(out * (1 - slippage))
3. BUILD + SEND (ix disc 6)
input/output vault by direction (zeroForOne -> vault0/vault1)
tickArrays = window from arrayStart(tick_current) walking direction
data = [6] u64(amountIn) u64(minOut) u128(0) u8(1) // baseIn, no limit
accounts: 10 head + bitmap_ext + tickArrays (table above)
sign + sendTransactionIntegrate a swap in 3 steps · Pulse
Pulse (anchored): posted-mid fill, no curve, no tick arrays.
1. DECODE POOL
getAccountInfo(pool); require owner == program && data[0] == 7
read price_x64@240, bid_factor@256, ask_factor@264, decimals@232/233,
status@234 (tradable when bit0 clear), haircut_bps_p1@235,
round_mode@236, vaults@136/168
2. QUOTE (posted mid +/- band, zero curve slippage; integer, see router.ts)
inputIsBase = inputMint == token_mint0
band = (inputIsBase ? bid_factor : ask_factor) || 1e12 // maker side (0=flat)
eff = price_x64 * band / 1e12
haircut_bps = data[235]==0 ? 5 : min(data[235]-1, 5) // SENTINEL @235
// round_mode @236: 1=PRECISE (fold 10^|dec| into ONE floor-divide), else LEGACY.
PRECISE base->quote: out = (amountIn*eff*10^max(d1-d0,0)) / (2^64*10^max(d0-d1,0))
PRECISE quote->base: out = (amountIn*2^64*10^max(d0-d1,0)) / (eff*10^max(d1-d0,0))
out = floor(out) * (10000 - haircut_bps) / 10000 // 0 if halted/stale
3. BUILD + SEND (ix disc 18)
input/output vault by direction
data = [18] u64(amountIn) u64(minOut)
accounts: 8 (payer, pool, user_in, user_out,
input_vault, output_vault, in_tprog, out_tprog)
sign + sendTransactionRead pools (RPC + gRPC)
Enumerate, snapshot, and stream every iso pool/position off-chain. memcmp the 1-byte disc at offset 0; single-byte disc base58 is one char (bs58Disc, e.g. disc 2 -> "3").
| Account | disc | memcmp byte (base58) |
|---|---|---|
| Apex pool | 2 | "3" |
| Pulse pool | 7 | "8" |
| position | 4 | "5" |
// every Apex (CLMM) pool: getProgramAccounts + memcmp disc 2 -> "3"
// Pulse pools -> disc 7 -> "8"; positions -> disc 4 -> "5"
curl $RPC -s -d '{
"jsonrpc":"2.0","id":1,"method":"getProgramAccounts",
"params":["isogrRJ6sEiktnfUY7QdZhDr5Sut3C8ER9HejjUqSZK",{
"encoding":"base64",
"filters":[{"memcmp":{"offset":0,"bytes":"3"}}]
}]
}'
// -> [{ pubkey, account:{ owner, data:[<base64>,"base64"] }}, ...]// one known pool -> base64 -> decode at the offsets above
curl $RPC -s -d '{
"jsonrpc":"2.0","id":1,"method":"getAccountInfo",
"params":["isoJANGQx1GmbmLUbGDa34Qxe6GLppFNxrZw5Kx5fX9",
{"encoding":"base64"}]
}'
// d = base64-decode(data[0]); require d[0] == 2 (Apex)
// sqrt_price_x64 = u128 LE @246 tick_current = i32 LE @262
// liquidity = u128 LE @230
// Pulse pool isoLWsMtAQT58VzufDJEiXGFV6PjiamtLwseHe6hBVw, d[0] == 7:
// price_x64 = u128 LE @240 ask_factor = u64 LE @264 (band)// Geyser push updates: filter accounts owned by the program. Add the
// disc memcmp to scope one engine. Endpoint is integrator-configured.
import Client from "@triton-one/yellowstone-grpc";
const client = new Client(GRPC_ENDPOINT, GRPC_TOKEN, undefined);
const stream = await client.subscribe();
stream.write({
accounts: {
iso: {
owner: ["isogrRJ6sEiktnfUY7QdZhDr5Sut3C8ER9HejjUqSZK"],
account: [],
// optional: one engine only (Apex). drop to stream all accounts.
filters: [{ memcmp: { offset: "0", base58: "3" } }],
},
},
slots: {}, transactions: {}, blocks: {}, blocksMeta: {}, entry: {},
accountsDataSlice: [], commitment: 1, // CONFIRMED
});
// each msg.account.account = { pubkey, owner, data(bytes), ... }
// decode data the same way as getAccountInfo above (disc @0).These are the exact reads the iso TS client performs (ui/src/lib/xmm: getProgramAccounts + memcmp in pools.ts, decodePoolClmm / decodePoolAnchor in state.ts).
Public REST APIopenrate limited
Open, no key, read-only (GET). Base /api/v1. CORS Access-Control-Allow-Origin: *. Per-IP token bucket, 60 req/min; every response carries X-RateLimit-Limit/Remaining/Reset, a 429 adds Retry-After. The reads are the iso TS client (listLivePools / loadPoolRow / quoteClmm) over RPC.
| Endpoint | Returns |
|---|---|
| GET /api/v1/pools | all live pools (engine, pair, price, fee, tvl, reserves, addresses) |
| GET /api/v1/pools/{id} | one pool (either engine); 404 if not an iso pool |
| GET /api/v1/quote?pool=&in=&amount= | Apex (CLMM) spot quote: out, price, feeBps, minReceived hint |
| GET /api/v1/oracle | first-party multi-source price oracle (baselines, pairs, freshness) |
| GET /api/v1/info | program id, pools, amm_config, IDL url, engines, version |
# iso-xmm public REST API base: https://xmm.mbco.io/api/v1
# Open, no key. CORS: Access-Control-Allow-Origin: *. Read-only (GET).
# Rate limit: 60 req/min per IP (token bucket). Every response sets:
# X-RateLimit-Limit: 60
# X-RateLimit-Remaining: <n>
# X-RateLimit-Reset: <unix-seconds when the bucket is full>
# Over the limit -> HTTP 429 { "error": "rate limit exceeded", "retryAfter": <s> }
# + Retry-After: <seconds>
## GET /api/v1/pools all live pools
curl -s https://xmm.mbco.io/api/v1/pools
# -> { count, pools: [ { name, id, engine, pair, price, feeBps, tvl, status,
# reserves:{base,quote}, addresses:{pool,ammConfig,baseMint,quoteMint,
# vault0,vault1,observation}, decimals:{base,quote}, tickCurrent, tickSpacing,
# liquidity, bandBps, confidence, priceUpdatedMs } ] }
## GET /api/v1/pools/{id} one pool (either engine); 404 if not an iso pool
curl -s https://xmm.mbco.io/api/v1/pools/isoJANGQx1GmbmLUbGDa34Qxe6GLppFNxrZw5Kx5fX9
# -> the single pool object (same shape as an element of /pools)
# 404 -> { "error": "not an iso pool", "id": "<addr>" }
## GET /api/v1/quote Apex (CLMM) spot quote
# pool iso CLMM pool address (clmm only; 400 for an anchored pool)
# in input token: symbol (SOL/USDC) OR mint address
# amount input amount, human units of `in`
# slippageBps optional, default 50 (0.5%) -> drives minReceived
curl -s 'https://xmm.mbco.io/api/v1/quote?pool=isoJANGQx1GmbmLUbGDa34Qxe6GLppFNxrZw5Kx5fX9&in=SOL&amount=1.5'
# -> { pool, engine, in:{token,amount}, out:{token,amount}, price, feeBps,
# inputIsBase, slippageBps, minReceived, note }
# minReceived = out * (1 - slippageBps/1e4); set as other_amount_threshold (raw)
# on the disc-6 swap ix (see Write operations below).
## GET /api/v1/oracle first-party multi-source price oracle (display mirror)
curl -s https://xmm.mbco.io/api/v1/oracle
# -> { asset:"USD", baselines:{ SOL,USDC,USDT,CHF: { price, conf, dispersion,
# median, sources:[{name,price,ageSec,kept}], fresh } },
# pairs:[ { pair, base, quote, mid, fresh } ], staleness, ts, cachedForMs }
# Recomputes the SAME aggregation the on-chain pricer posts; ~3s server cache.
## GET /api/v1/info program id, pools, amm_config, IDL url, engines, version
curl -s https://xmm.mbco.io/api/v1/info
# -> { name, version, build, programId, ammConfig, idlUrl, engines[], pools[], api{} }
# Errors are JSON: { "error": "<msg>", ... }. Statuses: 200 ok, 400 bad query,
# 404 not an iso pool, 429 rate limited, 502 upstream RPC error.Write operations (pool lifecycle)build + sign1-byte disc
The REST API is read-only. To write, build + sign these instructions yourself. disc = 1-byte ix at data[0]; body scalars LITTLE-endian; PDA index seeds BIG-endian (nonce tail u32 LE). Mirrored 1:1 from program/idl/xmm.json + ui/src/lib/xmm/ix.ts.
# iso-xmm WRITE operations (on-chain instruction recipes)
# The REST API is read-only. To WRITE, build + sign these instructions yourself.
# program isogrRJ6sEiktnfUY7QdZhDr5Sut3C8ER9HejjUqSZK
# data[0] = 1-byte ix disc; body scalars LITTLE-endian; string = u8 len + bytes.
# PDA index seeds BIG-endian; pool nonce tail u32 LE; ATA = [owner, tokenProgram, mint].
# token_program_{0,1} MUST equal the owner of mint_{0,1} (SPL vs Token-2022).
# mpl_core = CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d
# system = 11111111111111111111111111111111
## create_pool (disc 2) -> PoolStateClmm + 2 vaults + observation + bitmap_ext + Core collection
body: pool_nonce u32 | sqrt_price_x64 u128 | name (u8 len + bytes) | uri (u8 len + bytes)
accounts:
0 creator signer, w funds rent; Core collection signer
1 amm_config ro decoded for tick_spacing/fee
2 pool w PDA ["pool", amm_config, mint0, mint1, nonce_le]
3 token_mint_0 ro addr(mint0) < addr(mint1)
4 token_mint_1 ro
5 token_vault_0 w PDA ["pool_vault", pool, mint0]
6 token_vault_1 w PDA ["pool_vault", pool, mint1]
7 observation w PDA ["observation", pool]
8 bitmap_extension w PDA ["pool_tick_array_bitmap_extension", pool]
9 collection signer, w iso-vanity client keypair (Core collection)
10 token_program_0 ro == owner(mint0)
11 token_program_1 ro == owner(mint1)
12 mpl_core ro
13 system_program ro
# sqrt_price_x64 = floor(sqrt(price_raw) * 2^64), price_raw = token1_raw/token0_raw.
## create_anchor_pool (disc 16) -> PoolStateAnchor + 2 vaults (Engine B)
body: pool_nonce u32 | pricer_hot_key pubkey(32) | delta_cap_bps u64 | capacity_cap u64
| max_slot_lag u64 | max_age_ms u64 | s_floor u64 | cap_bps u64 | conf_max u64
| [guardian: option<pubkey>] | [max_band_bps: option<u64>]
# option = 1 byte (0 = none, 1 = present) then the value. Body 92 / 124 / 132 bytes.
accounts:
0 creator signer, w funds rent; pool authority (owner)
1 amm_config ro
2 pool w PDA ["pool_anchor", amm_config, mint0, mint1, nonce_le]
3 token_mint_0 ro addr(mint0) < addr(mint1)
4 token_mint_1 ro
5 token_vault_0 w PDA ["pool_vault", pool, mint0]
6 token_vault_1 w PDA ["pool_vault", pool, mint1]
7 token_program_0 ro == owner(mint0)
8 token_program_1 ro == owner(mint1)
9 system_program ro
# Then disc 17 post_price (pricer_hot_key signs) seeds the first quote; disc 19
# deposit_inventory funds the vaults. SwapAnchor (disc 18) is read-side in /docs above.
## open_position (disc 3) -> PositionState PDA + Core NFT, liquidity starts 0
body: tick_lower_index i32 | tick_upper_index i32 (lower < upper, on tick_spacing)
accounts:
0 payer signer, w funds rent; NFT owner; rent_receiver
1 pool ro decoded for tick_spacing
2 position w PDA ["position", asset]
3 asset signer, w iso-vanity client keypair (Core NFT)
4 collection w the pool's Core collection
5 tick_array_lower w PDA ["tick_array", pool, lower_start_be]
6 tick_array_upper w PDA ["tick_array", pool, upper_start_be]
7 mpl_core ro
8 system_program ro
# *_start = floor-to-neg-inf of the boundary tick over (tick_spacing * 60) ticks.
## increase_liquidity (disc 4) -> deposit (amounts round UP)
body: amount_0_max u64 | amount_1_max u64
accounts:
0 owner signer gated by asset ownership
1 pool w
2 position w PDA ["position", asset]
3 asset ro Core NFT (ownership gate)
4 tick_array_lower w
5 tick_array_upper w
6 token_owner_0 w user src ATA (mint0)
7 token_owner_1 w user src ATA (mint1)
8 token_vault_0 w == pool.token_vault_0
9 token_vault_1 w == pool.token_vault_1
10 token_program_0 ro
11 token_program_1 ro
12 bitmap_extension w OPTIONAL ["pool_tick_array_bitmap_extension", pool]
# bitmap_extension only when a boundary array is outside the pool-internal window.
## decrease_liquidity (disc 5) -> withdraw (amounts round DOWN; pool PDA signs vault-out)
body: liquidity u128 | amount_0_min u64 | amount_1_min u64
accounts: same 13 as increase_liquidity (idx 12 bitmap_extension OPTIONAL),
token_owner_{0,1} are the user DST ATAs here.
## collect_fees (disc 7) -> pay owed fees to the owner, re-snapshot fee_growth_inside
body: (none)
accounts:
0 owner signer ownership gate
1 pool ro fee_growth source; PDA signs vault-out
2 position w PDA ["position", asset]
3 asset ro Core NFT
4 token_owner_0 w user dst ATA (mint0)
5 token_owner_1 w user dst ATA (mint1)
6 token_vault_0 w
7 token_vault_1 w
8 token_program_0 ro
9 token_program_1 ro
10 tick_array_lower ro fee_growth_outside source
11 tick_array_upper ro
## close_position (disc 8) -> burn the Core NFT, reclaim rent (liquidity==0 AND owed==0)
body: (none)
accounts:
0 owner signer, w ownership gate; BurnV1 authority
1 position w PDA ["position", asset]
2 asset w Core NFT to burn
3 collection w the pool's Core collection
4 rent_receiver w == position.rent_receiver
5 mpl_core ro
6 system_program ro
## close_pool (disc 22) -> owner teardown of an EMPTY + PAUSED pool (set_status first)
body: (none)
accounts (CLMM, 11):
0 authority signer == pool.owner
1 pool w PDA signs vault sweep + close
2 observation w OPTIONAL CLMM only: == pool.observation_key
3 bitmap_extension w OPTIONAL CLMM only
4 vault_0 w
5 vault_1 w
6 dst_0 w owner ATA(mint0) sweep target
7 dst_1 w owner ATA(mint1) sweep target
8 rent_receiver w
9 token_program_0 ro
10 token_program_1 ro
# Anchored pool: omit observation + bitmap_extension (9 accounts; rest shift down by 2).
# Pre-req: disc 21 set_status(bit, disable=1) to HALT, and the pool must be empty.
# Lifecycle order:
# CLMM: create_pool -> open_position -> increase_liquidity -> (swap disc 6 / collect_fees)
# -> decrease_liquidity -> close_position -> set_status(halt) -> close_pool
# Anchor: create_anchor_pool -> post_price (disc 17) -> deposit_inventory (disc 19)
# -> (swap_anchor disc 18) -> withdraw_inventory (disc 20) -> set_status -> close_pool