Skip to main content

Trading Pairs

Every fr* asset trades against frBTC on SUBFROSTAMM, SUBFROST's constant product AMM on alkanes. There is one pool per asset, all created through the same factory. BTC enters and leaves through frBTC (32:0), so a single Bitcoin transaction can wrap BTC and trade or add liquidity in one step.

Status

All four pools are live on mainnet. SUBFROST seeded frSOL/frBTC (2:103202), frXRP/frBTC (2:103203) and frADA/frBTC (2:103204) with about $480 a side each, at market, in blocks 971467 to 971469. The frFB/frBTC pool 2:102725 is also live; see the Fractal docs. These are the only SUBFROSTAMM pools for these pairs. FindPool on the factory (below) returns the same ids; any other id is not ours.

Contracts​

contractidnotes
SUBFROSTAMM factory (router)4:333052creates and finds pools, routes swaps and liquidity
frBTC32:08 decimals. Opcode 77 wraps BTC, opcode 78 unwraps
frSOL4:50050lamport, 9 decimals
frXRP4:50051drop, 6 decimals
frADA4:50052lovelace, 6 decimals
frFB4:888888 decimals
poolpool idreserve order
frSOL/frBTC2:103202(frSOL, frBTC)
frXRP/frBTC2:103203(frXRP, frBTC)
frADA/frBTC2:103204(frADA, frBTC)
frFB/frBTC2:102725(frFB, frBTC)

A pool's coins are stored in sorted id order, so the fr* asset (block 4) is always coin 0 and frBTC (block 32) is coin 1. The pool id is also the id of its LP token.

Finding a pool​

FindPool is opcode 2 on the factory, with both coin ids. It returns the pool id as two u128 LE words, or reverts if the pair has no pool.

alkanes-cli -p mainnet --jsonrpc-url https://mainnet.subfrost.io/v4/jsonrpc \
alkanes simulate 4:333052:2:4:50051:32:0 # FindPool(frXRP, frBTC)

Opcode 3 (GetAllPools) returns a count followed by every pool id. subfrost-cli pools and subfrost-cli pool <BLOCK:TX> show the same pools with reserves already decoded.

Reading a pool​

pool opcodenamereturns
97GetReservesreserve0, reserve1, lp_ppm, protocol_ppm, season_ppm, height_last, each a u128 LE
20GetFeeslp_ppm, protocol_ppm, season_ppm
24GetAmountOut(i, amount_in)the output for amount_in of coin i, at the current reserves
101GetTotalSupplyLP supply
98GetPriceCumulativeLastTWAP accumulators (Q112, price times blocks)
999PoolDetailsOYL compatible pool info, then SUBFROSTAMM fields
alkanes-cli -p mainnet --jsonrpc-url https://mainnet.subfrost.io/v4/jsonrpc \
alkanes simulate 2:<pool>:97 # reserves and fees

Quoting​

The swap fee is the sum of the three ppm values. For an exact input in of one coin:

fee  = lp_ppm + protocol_ppm + season_ppm
out = in · (1e6 − fee) · R_out / (R_in · 1e6 + in · (1e6 − fee))

Opcode 24 gives the same number from the pool itself. Quote from the reserves at the height you build at, set amount_out_min from your slippage tolerance, and simulate before signing.

Depth

These pools start small. A trade moves the price by roughly amount / reserve, so on a $1,000 pool a $50 trade moves it about 10%. Always pass a real amount_out_min.

Swapping​

Swaps go through the factory, never the pool directly.

factory opcodecall
29SwapExactTokensForTokensImplicit(amount_out_min, deadline, path_len, path…): swaps everything of path[0] that arrived with the call
13SwapExactTokensForTokens(amount_in, amount_out_min, deadline, path_len, path…)
14SwapTokensForExactTokens(amount_out, amount_in_max, deadline, path_len, path…)

path_len is the number of alkane ids in the path (hops plus one) and is required, followed by 2 · path_len words. deadline is a block height; 340282366920938463463374607431768211455 (u128::MAX) means none. Prefer opcode 29 when you send the exact input in the parcel. Do not use opcode 13 with amount_in = 0: it does not mean "everything" and runs out of fuel.

frXRP to frBTC:

[4, 333052, 29, amount_out_min, deadline, 2, 4, 50051, 32, 0]

frSOL to frXRP is two hops through frBTC (path_len 3):

[4, 333052, 29, amount_out_min, deadline, 3, 4, 50050, 32, 0, 4, 50051]

To reach BTC, finish with a frBTC unwrap ([32, 0, 78, signer_vout, deadline], see Wrapping frBTC). For DIESEL, FIRE or BTCUSD, hop from frBTC into those pools.

BTC in, fr* asset out, one transaction​

Wrapping is opcode 77 on frBTC: the transaction pays BTC to the frBTC signer address, and the minted frBTC goes to the protostone's pointer. Point the wrap at the swap protostone:

subfrost-cli tx build \
--output signer:frbtc:600000 \
--output self \
--protostones '[32,0,77]:p1:v1,[4,333052,29,<min_out>,<deadline>,2,32,0,4,50051]:v1:v1' \
--fee-rate 2 --out plan.json
subfrost-cli tx simulate plan.json
subfrost-cli tx sign plan.json && subfrost-cli tx broadcast plan.json

Here v0 is the frBTC signer output carrying the BTC to wrap and v1 is your own output, which receives the frXRP and any refund. subfrost-cli tx inspect plan.json names every output and protostone target before you sign, and tx run does build, inspect, simulate, sign and broadcast in one go.

Liquidity​

factory opcodecall
11AddLiquidity(a.block, a.tx, b.block, b.tx, a_min, b_min, min_lp, deadline): both coins in the parcel; the excess of the over supplied coin is returned
12RemoveLiquidity(a.block, a.tx, b.block, b.tx, a_min, b_min, deadline): burns every LP unit in the parcel
1CreatePool(a.block, a.tx, b.block, b.tx, min_lp): permissionless, once per pair; the seed coins ride in the parcel

Size an add at the pool's current ratio: b = a · reserve1 / reserve0. The LP minted is min(a · supply / reserve0, b · supply / reserve1), read supply from opcode 101. For the first deposit (CreatePool) it is isqrt(a · b) − 1000, with 1000 locked forever.

Wrap BTC and add liquidity, one transaction​

Hold the fr* asset, bring BTC, and get LP back in one transaction. The first protostone moves the asset to the add, the second wraps BTC into it, the third adds:

subfrost-cli tx build \
--output signer:frbtc:<sats> \
--output self \
--spend 4:50051:<drops> \
--protostones '[4:50051:<drops>:p2]:v1:v1,[32,0,77]:p2:v1,[4,333052,11,4,50051,32,0,<a_min>,<b_min>,<min_lp>,<deadline>]:v1:v1' \
--fee-rate 2 --out plan.json

The LP and the unused part of either side return to v1. Removing is the reverse: spend the LP (--spend 2:<pool>:all), call opcode 12, and unwrap the frBTC to BTC in a following protostone if you want BTC.

Routed deposits into the pools​

The frFB intents already turn a deposit on another chain into a finished position on Bitcoin: swap to frBTC, BTC, DIESEL, FIRE, a stablecoin through BTCUSD (4:1778), or an LP position, with a share kept in BTC or DIESEL for later fees. The same route intents are coming to frXRP first, then frADA and frSOL, landing in the pools on this page:

  • LP. Part of the deposit is swapped to frBTC and both sides are added, so an XRP deposit arrives as frXRP/frBTC LP. If the pool is too thin for the deposit, the app falls back to a plain mint and asks you to bring BTC to pair with it, the flow above.
  • Swaps. frBTC, BTC, DIESEL and FIRE, all through the asset's frBTC pool.
  • STABLE. Always routed through BTCUSD (4:1778), then out to USDC or USDT on EVM chains.

Until routes are switched on for an asset, its deposits mint plainly to the recipient and every trade on this page is done by the holder.

Checklist for integrators​

  • Take pool ids from this page (or FindPool), never from a token list, a chat message or a link, and check that FindPool on 4:333052 returns the same id. A pool that FindPool doesn't return is not the SUBFROSTAMM pool for that pair.
  • Quote from opcode 97 at the height you build, and always set amount_out_min, a_min, b_min and min_lp.
  • Simulate every transaction (subfrost-cli tx simulate, or alkanes_simulate) and refuse to sign on a revert.
  • Units differ per asset: 9 decimals for frSOL, 6 for frXRP and frADA, 8 for frBTC and frFB.
  • See the developer reference for deposits and burns, and Agents and automation for bots.