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
- Deposit. You send FB to the gateway address on Fractal.
- Sweep. Anyone (normally the gateway service) moves the deposit into fb-vault (
22:22) with the covenant spend. The deposit pays its own sweep feeK. - 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. - 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
| route | what you receive at the recipient | gas it may carry |
|---|---|---|
| plain | frFB 4:88888 | DIESEL or BTC (optional) |
| BTC | BTC, unwrapped from frBTC and paid by the frBTC signer | none (it already delivers BTC) |
| DIESEL | DIESEL 2:0 (frFB → frBTC → DIESEL on the OYL pool 2:77087) | BTC only |
| LP | frFB/frBTC LP tokens (2:102725) plus any excess of either side | DIESEL or BTC |
| FIRE | FIRE 2:77623 (frFB → frBTC → FIRE on the SUBFROSTAMM pool 2:97417) | DIESEL or BTC |
| frUSD | frUSD 4:1776 kept on Bitcoin (frFB → frBTC → frUSD on the BTCUSD pool 4:1778) | DIESEL or BTC |
| STABLE | USDC 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:
| chain | chain id | USDC | USDT |
|---|---|---|---|
| Base | 8453 | yes | yes |
| Polygon | 137 | yes | yes |
| BNB Chain | 56 | yes | yes |
- 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
GasDropHandlercontract 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'sPegOutSentpaying 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 USDTchecks 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 route | gas it may carry |
|---|---|
| plain, LP, FIRE, frUSD, STABLE | DIESEL or BTC |
| DIESEL | BTC only (DIESEL is already the gas token) |
| BTC | none (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 K | 2,000 sats by default (1,000–100,000), fixed per address, taken on Fractal |
| mint fee | 0.30% + 1,000 sats, taken from the frFB minted |
| route fee | 6 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 fee | an optional extra fee on routes with a gas leg, published as routes.gas_fee_sats; currently 0 |
| routed minimum | 7 FB per routed deposit; 30 FB for STABLE (the burn needs at least 10 frUSD) |
| BTC unwrap floor | the 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 fees | SUBFROSTAMM 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 margin | clients 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:
| leg | quote |
|---|---|
| SUBFROSTAMM swap | out = 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 exchange | get_dy(0, 1, dx), opcode 107 on 4:1778 |
| OYL swap | constant product at 1%; pool 2:77087 opcode 97 returns the (DIESEL, frBTC) reserves |
| LP | the 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 legs | the 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
| case | the recipient receives |
|---|---|
| the route completes | the 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 floor | the main route, with the gas share added to it. For plain + gas: plain frFB |
| a BTC route would hand its unwrap less than the floor | plain 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 decode | plain 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:
| leg | fuel |
|---|---|
| mint | 0.38M |
| SUBFROSTAMM swap | 2.15M |
| add liquidity | 2.69M |
| BTCUSD exchange | 4.67M |
| OYL swap | 4.67M |
| frUSD burn | 0.91M |
| unwrap | 0.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:
| stage | chain | |
|---|---|---|
| 1 | Fractal | deposit seen |
| 2 | Fractal | deposit confirmed |
| 3 | Fractal | swept into fb-vault |
| 4 | Fractal | credited by fb-vault |
| 5 | Bitcoin | frFB mint in the mempool |
| 6 | Bitcoin | mint confirmed |
| 7 | Bitcoin | frFB 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/:addresson the gateway service). - The mint: the Bitcoin transaction from the group's address whose frFB
Mintcall 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
| kind | name | fields | version |
|---|---|---|---|
1 | BTC | min_frbtc | 1 |
2 | STABLE | min_frbtc, min_frusd, n, burn_args[n] | 1 (2 with gas) |
3 | DIESEL | min_frbtc, min_diesel | 1 (2 with gas) |
4 | LP | swap_ppm, min_frbtc, a_min_frfb, b_min_frbtc, min_lp | 1 (2 with gas) |
5 | FIRE | min_frbtc, min_fire | 2 |
6 | FRUSD | min_frbtc, min_frusd | 2 |
7 | PLAIN | none (only with gas) | 2 |
| gas | fields |
|---|---|
0 none | none |
1 DIESEL | gas_ppm, min_frbtc, min_diesel |
2 BTC | gas_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_ppmis 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/vaultreportsroutes.max_action_version=2, as it does now.
Examples:
| bytes | action |
|---|---|
0104d8d91e8a5694f187eb088a56d0ffb702 | LP: 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 |
0205008a5680feac0d | FIRE: min_frbtc 11,018, min_fire 28,000,000 |
0206008a5680cee4cd02 | frUSD: min_frbtc 11,018, min_frusd 700,000,000 |
0203020506a08d06d804 | DIESEL (5, 6) + BTC gas: 100,000 ppm, min_frbtc 600 |
020702c09a0cd00f | plain 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 = 0runs 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.