Skip to main content

Intent Routes

A gateway address commits to an intent: a recipient on Bitcoin and an optional action. With no action, the signer group mints plain frFB to the recipient. With an action, the group carries the route out on Bitcoin as part of the mint. You send FB to the gateway address from any wallet (UniSat, an exchange withdrawal, a script) and sign nothing else.

The action is part of the intent bytes, so it is part of the address: every (route, recipient, bounds) combination has its own deposit address.

Every route below has run on mainnet. The transactions are in Mainnet examples.

How a deposit becomes a route​

  1. Deposit. You send FB to the gateway address on Fractal.
  2. Sweep. Anyone (normally the gateway service) moves the deposit into fb-vault (22:22) with the covenant spend. The deposit pays its own sweep fee K.
  3. Mint and route. The FROST signer group mints frFB (4:88888) on Bitcoin. In the same Bitcoin transaction, and in a short chain of child transactions when the route needs more execution budget than one transaction gets, it runs the route: swaps, liquidity adds, frBTC unwraps, or the frUSD bridge-out.
  4. Delivery. The result lands at your recipient script. BTC (from a BTC route or a BTC gas leg) is paid shortly afterwards by the frBTC signer. Stablecoins on an EVM chain arrive through the frUSD peg-out.

Redemption (frFB back to FB on Fractal) is unchanged: see Redemption.

The routes​

routewhat you receive at the recipientgas it may carry
plainfrFB 4:88888DIESEL or BTC (optional)
BTCBTC, unwrapped from frBTC and paid by the frBTC signernone (it already delivers BTC)
DIESELDIESEL 2:0 (frFB → frBTC → DIESEL on the OYL pool 2:77087)BTC only
LPfrFB/frBTC LP tokens (2:102725) plus any excess of either sideDIESEL or BTC
FIREFIRE 2:77623 (frFB → frBTC → FIRE on the SUBFROSTAMM pool 2:97417)DIESEL or BTC
frUSDfrUSD 4:1776 kept on Bitcoin (frFB → frBTC → frUSD on the BTCUSD pool 4:1778)DIESEL or BTC
STABLEUSDC or USDT on Base, Polygon or BNB Chain (frFB → frBTC → frUSD → frUSD burn-and-bridge, paid out through Across)DIESEL or BTC, delivered on Bitcoin to the recipient

Every swap goes through frBTC first: frFB → frBTC on the SUBFROSTAMM frFB/frBTC pool 2:102725 (reached through the SUBFROSTAMM factory 4:333052), then on to the target.

  • frUSD keeps the frUSD on Bitcoin at your recipient: no burn, no bridge.
  • The recipient must be a Bitcoin script you control, even for STABLE. Every refund goes there (see Outcomes).

STABLE: USDC / USDT on an EVM chain​

The frUSD is burned on Bitcoin with frUSD's burn-and-bridge call (4:1776 opcode 14), and the peg-out router pays USDC or USDT on the chain you choose, through Across:

chainchain idUSDCUSDT
Base8453yesyes
Polygon137yesyes
BNB Chain56yesyes
  • Gas drop. A STABLE payout can carry a destination gas drop: a share of the payout, from 1 to 1,000 basis points and up to the handler's cap, is swapped to the destination chain's native token (ETH on Base, POL on Polygon, BNB on BNB Chain) and sent with the stablecoin. The payout is routed through a per-intent GasDropHandler contract whose address is derived from the payee, the share, the slippage and the cap.
  • The EVM recipient signs nothing. The gas-drop registration is unsigned: your client posts the handler's configuration to the peg-out service just before the FB deposit is signed. The service holds it as pending, and admits it only when it sees the peg-out router's PegOutSent paying that handler on Ethereum. The payee can be any address, including one you don't control.
  • The burn needs at least 10 frUSD, which is why STABLE deposits start at 30 FB.
  • subfrost-cli fractal stable-check --to-chain 56 --to-token USDT checks that a payout path is being served before you deposit.

The gas split​

A route can turn part of its value into gas for the recipient, so a fresh wallet can transact right away:

  • DIESEL gas: frFB → frBTC → DIESEL on the OYL pool 2:77087.
  • BTC gas: frFB → frBTC → unwrapped BTC, paid to the recipient by the frBTC signer.

You choose a share of the routed frFB. On the wire it is in parts per million; the CLI takes basis points from 1 to 5,000 (--gas-bps 2000 = 20% = 200,000 ppm). The rest runs the main route.

main routegas it may carry
plain, LP, FIRE, frUSD, STABLEDIESEL or BTC
DIESELBTC only (DIESEL is already the gas token)
BTCnone (it already delivers BTC)

A gas leg can never lose value. The signer group replays the transaction before signing. If the replay shows the gas leg would not complete, because a gas swap would miss its minimum or a BTC gas unwrap would fall under the unwrap floor, the group drops only the gas leg and its share joins the main route. A plain + gas deposit whose gas leg is dropped is delivered as plain frFB. The deposit's record carries a note saying gas leg dropped (…). Clients refuse to build a BTC gas leg whose minimum would not clear the floor (see below).

Minimums and fees​

amount (current settings)
gateway sweep fee K2,000 sats by default (1,000–100,000), fixed per address, taken on Fractal
mint fee0.30% + 1,000 sats, taken from the frFB minted
route fee6 FB (600,000,000 frFB units) per routed deposit, withheld from the minted frFB. It pays the Bitcoin fees of the extra legs. Published as routes.fee_sats in the gateway service's /v1/vault
gas feean optional extra fee on routes with a gas leg, published as routes.gas_fee_sats; currently 0
routed minimum7 FB per routed deposit; 30 FB for STABLE (the burn needs at least 10 frUSD)
BTC unwrap floorthe frBTC reaching an unwrap (a BTC route, or a BTC gas leg) must be at least 1,000 frBTC sats, the routes.btc_min_frbtc the gateway service publishes. The signers' own hard floor is 547 (546 dust + the 0.1% unwrap premium). Below it a BTC route is delivered as plain frFB, and a BTC gas leg is dropped
pool feesSUBFROSTAMM 1.5% per swap (frFB/frBTC, FIRE/frBTC); OYL 1% (DIESEL); the BTCUSD CryptoSwap pool's own fee; for STABLE, the frUSD vault and Across fees
slippage marginclients quote every leg live and take 300–500 bps off each minimum (default 500)

The routed amount is:

minted     = (deposit − K) − ⌊(deposit − K) × 0.30%⌋ − 1,000
routed net = minted − route fee [− gas fee]
gas share = ⌊routed net × gas_ppm / 1,000,000⌋
main share = routed net − gas share

For example, 50 FB to a FIRE + 10% DIESEL gas address: the vault receives 49.99998 FB, the mint fee leaves 49.84997006 FB, the route fee leaves 43.84997006 FB routed: 4.38499700 FB buys DIESEL gas, 39.46497306 FB buys FIRE.

At current prices a BTC route needs roughly 10 FB or more: after the 6 FB route fee, a 7 FB deposit buys fewer than 1,000 frBTC sats.

Bounds: absolute and loose​

About 20 minutes pass between registering an address and the mint (the sweep, the signers' confirmation depth, a Bitcoin block). So the client quotes every leg for the amount that will be minted, takes the margin off, and writes absolute minimums into the action. The signers do not re-quote: they replay the whole transaction before signing, and anything that would miss a minimum is dropped (see Outcomes). A tighter margin risks a dropped route; a looser one only costs price.

How each leg is quoted:

legquote
SUBFROSTAMM swapout = in·(1e6 − fee)·R_out / (R_in·1e6 + in·(1e6 − fee)). The pool's opcode 97 returns reserve0, reserve1, lp_ppm, protocol_ppm, season_ppm; fee = the sum of the three ppm values. Reserves are in sorted coin order: (frFB, frBTC) for 2:102725, (FIRE, frBTC) for 2:97417
BTCUSD exchangeget_dy(0, 1, dx), opcode 107 on 4:1778
OYL swapconstant product at 1%; pool 2:77087 opcode 97 returns the (DIESEL, frBTC) reserves
LPthe zap split below, then the amounts added and the LP minted, from the post-swap reserves and the pool's LP supply (opcode 101)
gas legsthe gas share, quoted on the frFB/frBTC reserves the main swap leaves behind (the gas chain runs after it)

The LP route's swap_ppm is the single-sided zap split. For a deposit of A into reserves R with fee fraction f (taking g = 1 − f), the amount to swap is:

s = (√(R²(1+g)² + 4gRA) − R(1+g)) / 2g

Outcomes​

casethe recipient receives
the route completesthe route's asset (BTC / DIESEL / LP + excess / FIRE / frUSD / the stablecoin on EVM), plus the gas leg's DIESEL or BTC
the main route would fail before signing (the replay says so, or there is no replay answer)plain frFB: the route is dropped and the deposit minted normally
a gas leg would fail before signing, or a BTC gas unwrap would be under the floorthe main route, with the gas share added to it. For plain + gas: plain frFB
a BTC route would hand its unwrap less than the floorplain frFB
a leg fails on chain (the price moved past a bound after signing)whatever the route had reached: frFB if the first swap failed, frBTC or frUSD if a later leg did. Every step refunds to the recipient
routes are off, or the action doesn't decodeplain frFB

Nothing is ever lost to a route. The worst case is holding frFB, or the intermediate token, at your own address.

On chain: one mint, often a short chain​

Alkanes gives every Bitcoin transaction at least 3.5M fuel however small it is, and only a share of more depending on how busy the block is. So the signer group never bets on block share. It measures each leg's fuel in its pre-signing replay and packs legs into transactions that each stay under 2.8M.

Measured on mainnet:

legfuel
mint0.38M
SUBFROSTAMM swap2.15M
add liquidity2.69M
BTCUSD exchange4.67M
OYL swap4.67M
frUSD burn0.91M
unwrap0.51M
  • Composed, when everything fits: one mint transaction carrying the route.

  • Split, when it doesn't (today, every route): the mint carries the first swap and a small group-held escrow output, pre-funded with the BTC the rest will spend. Child transactions spend the escrow and run the remaining legs, each paying for its parent (CPFP), so they normally confirm in the same block.

    mint     Mint · routing · first swap ─▶ escrow (group)
    child 1 escrow ─▶ next leg(s) ─▶ escrow′ (group) or the recipient
    child 2 …

    The chain is always linear. Every child has the recipient's output first, and every leg refunds there. A route with a gas split is typically the mint plus two or three children: FIRE + BTC gas is the mint and 2 children, LP + DIESEL gas the mint and 3.

  • If a child never lands, its escrow sits on the group's address holding the reached token. Before minting anything else, the group sends the whole escrow to the recipient it reads back out of the transaction that created it.

  • One routed deposit per mint. A second one is minted in the next round.

Tracking a routed deposit​

subfrost-cli fractal track follows all of this for you (see the CLI). The stages are:

stagechain
1Fractaldeposit seen
2Fractaldeposit confirmed
3Fractalswept into fb-vault
4Fractalcredited by fb-vault
5BitcoinfrFB mint in the mempool
6Bitcoinmint confirmed
7BitcoinfrFB indexed (done)

then, for a routed deposit, the route's delivery: the mint and its children, what each one left at the recipient, and the gas leg's state.

To follow it by hand:

  • On Fractal: the gateway address and its sweep, as for any deposit (GET /v1/gateway/:address on the gateway service).
  • The mint: the Bitcoin transaction from the group's address whose frFB Mint call carries the marker covering your deposit. A routed deposit's frFB is not sent to its output directly: it goes into the first route leg, or into the escrow when split.
  • Children: the escrow is the group's output after the change output. Follow its spend to child 1, then each child's group output to the next. The last child's first output is your recipient, holding the route's result; the gas leg's DIESEL lands at the recipient output of the child that runs it.
  • BTC (a BTC route or BTC gas) arrives in a later Bitcoin transaction from the frBTC signer.
  • STABLE: after the burn, the payout follows the frUSD peg-out, keyed by the burn transaction (subfrost-cli pegout track).
  • A dropped route looks exactly like a plain deposit: frFB at the recipient, in the mint.

For developers: the action format​

An action is a version byte, a kind byte, and (version 2) a gas byte, followed by LEB128 integers: exactly the kind's fields, then the gas fields, with nothing after them. Contract ids are never in the action; the signers take them from their own configuration.

v1: 0x01 | kind | fields
v2: 0x02 | kind | gas | fields | gas fields
kindnamefieldsversion
1BTCmin_frbtc1
2STABLEmin_frbtc, min_frusd, n, burn_args[n]1 (2 with gas)
3DIESELmin_frbtc, min_diesel1 (2 with gas)
4LPswap_ppm, min_frbtc, a_min_frfb, b_min_frbtc, min_lp1 (2 with gas)
5FIREmin_frbtc, min_fire2
6FRUSDmin_frbtc, min_frusd2
7PLAINnone (only with gas)2
gasfields
0 nonenone
1 DIESELgas_ppm, min_frbtc, min_diesel
2 BTCgas_ppm, min_frbtc
  • burn_args (STABLE) are the frUSD burn arguments after the target and opcode: the EVM recipient, the asset (0 USDC, 1 USDT), and the peg-out router payload (route index, recipient, refund address, fee, deadline). The burn takes all the frUSD it receives.
  • gas_ppm is 1 to 999,999.
  • Kinds 1–4 without gas are always written as version 1, so every route has exactly one encoding and one address. Version-2 decoding is canonical: the bytes must re-encode to themselves.
  • A signer that predates version 2 reads it as an unknown version and delivers plain frFB: it never misreads it. Clients build version-2 actions only when the gateway service's /v1/vault reports routes.max_action_version = 2, as it does now.

Examples:

bytesaction
0104d8d91e8a5694f187eb088a56d0ffb702LP: swap 50.3% (swap_ppm 503,000), min_frbtc 11,018, 2,372,008,084 frFB and 11,018 frBTC added, min_lp 5,111,760
0205008a5680feac0dFIRE: min_frbtc 11,018, min_fire 28,000,000
0206008a5680cee4cd02frUSD: min_frbtc 11,018, min_frusd 700,000,000
0203020506a08d06d804DIESEL (5, 6) + BTC gas: 100,000 ppm, min_frbtc 600
020702c09a0cd00fplain frFB + 20% BTC gas, min_frbtc 2,000

Put the action in the intent's action field and register the gateway address exactly as for a plain intent (see the developer guide).

The reference encoder is fractalplane programs/frfb-core/src/action.rs. The SUBFROST wallet core (subfrost-fractal) vendors it byte for byte and checks the same test vectors.

Contract notes​

  • SUBFROSTAMM swaps use opcode 29 (swap everything received). Opcode 13 with amount_in = 0 runs out of fuel on mainnet; pass an explicit amount if you use opcode 13 yourself.
  • SUBFROSTAMM deadlines: a deadline of 0 always reverts; routes pass the maximum value.
  • frBTC unwrap must be a top-level protostone with no edicts, in a transaction with an output paying the frBTC signer. The group adds that output.