Tollbooth docs
Tollbooth is a token exchange on Solana. Every pool auctions the right to set its swap fee. The winner, called the operator, picks a fee between a floor and a cap that never change. The operator keeps whatever it charges above the floor and pays rent, every slot, to the pool's liquidity providers.
The design follows the am-AMM paper by Adams, Moallemi, Reynolds and Robinson. The point is that whoever can price a pool best, usually an arbitrageur, competes for the booth in rent, and that rent goes to LPs instead of being lost to them.
| Who | What they do | What they get |
|---|---|---|
| Traders | Swap like on any constant-product pool | A fee that can't go above the pool's cap |
| Operators | Bid rent in SOL to run a pool's booth, then set its fee | The toll: the fee above the floor on every trade |
| Liquidity providers | Deposit both tokens of a pool | The floor fee on every trade, plus 90% of the rent |
| $TOLL holders | Hold the protocol coin | 10% of all rent buys $TOLL and burns it |
One trade, step by step
Say you swap 10 SOL for $ROAD in a pool holding 800 SOL, the floor is 0.30% and the operator has set the fee to 1.20%.
- The booth reads the fee. It is 1.20%, the fee the operator set in an earlier slot. A fee change never lands in the same slot as the trade it applies to, so nobody can raise it in front of you.
- The floor stays with LPs. 0.30% of the input, 0.03 SOL, stays in the pool like any AMM fee. Everyone pays it, the operator included.
- The toll goes to the operator. The other 0.90%, 0.09 SOL, moves to the operator's toll account for this pool.
- The curve moves the price. The remaining 9.88 SOL goes through
x ยท y = k. - Tokens move once. You get your $ROAD out, or, if your minimum isn't met, the whole transaction fails and nothing moves.
When nobody runs the booth, the pool is quiet: it charges only the floor and all of it stays with LPs.
Floor and cap
Each pool fixes a fee floor and a fee cap when it is created. The cap can be at most 10%. The operator can set any fee in between, and a change applies from the next slot. Nothing about a pool's tokens, floor, cap or bid delay can change after creation, not even by the creator.
A fee outside the band is refused by the program. A trader can always read the cap on the pool account and knows the worst case before trading.
The booth auction
- Bids are rent. A bid names a rent in lamports per slot, a deposit, and the fee the bidder will charge. The page shows rent in SOL per minute; a minute is about 150 slots.
- Beat it by 10%. A new bid must be at least 10% above the highest rent, whether that's the operator's or a waiting challenger's.
- Cover the delay. The deposit must pay for at least the pool's delay, normally 150 slots, of your rent.
- Wait the delay. A winning bid waits that many slots before it takes over, so nobody grabs the booth in the middle of a trade.
- Refunds. An outbid challenger gets its whole deposit back. A replaced operator gets back what's left of its deposit.
- Running out. Rent is taken from the deposit every slot. When it runs out, a waiting challenger takes over; if nobody is waiting, the booth goes quiet.
- Operator tools. The operator can top up the deposit, change the fee inside the band, and collect its tolls at any time.
Liquidity providers
LPs earn two things: the floor fee on every trade, which stays in the pool, and 90% of the booth's rent, which streams to them in SOL by share every slot. Rent is claimable at any time and is paid out automatically when you withdraw.
On a quiet day a fixed-fee pool earns almost nothing. A Tollbooth pool still earns rent, because the operator pays for the booth whether or not volume shows up.
A position can be locked until a date. Locks can only be extended; nobody can shorten one, the owner included. Coin creators use this to show their liquidity can't be pulled.
$TOLL buyback and burn
The other 10% of rent goes to the treasury, which can do exactly one thing: buy $TOLL through the TOLL / SOL Tollbooth pool and burn it in the same instruction. Anyone can trigger a buyback. Each one:
- waits a fixed interval after the previous one, about 15 minutes;
- spends at most 10% of the treasury;
- moves the TOLL / SOL price by at most about 1%;
- pays only the pool's floor fee, never an operator's toll.
Launching a coin
The launch form creates a coin and its Tollbooth pool in one transaction: it creates a Token-2022 mint with the name and ticker on the mint itself, mints the whole supply, puts the supply and your SOL into a new pool, and drops the mint authority so nobody can ever mint more.
For the first 150 slots the fee sits at the pool's cap and none of it is a toll: it all goes to LPs, so launch snipers pay the pool. After that the booth opens for bids.
Pools refuse Token-2022 mints with extensions that could take tokens from the pool or freeze it, such as transfer fees, transfer hooks, permanent delegates, default-frozen accounts, non-transferable tokens and confidential transfers.
Program and SDK
The program is written with Anchor. On devnet its address is CUVu4X8eEZ1GHQCvw8ZNQcUCeHfRYiBY5hKDP9bZ3JTb (Explorer). Every pool uses the same account layout, so a router integrates once.
Accounts
| Account | Seeds |
|---|---|
| Pool | ["pool", mint_a, mint_b], mints sorted by bytes |
| Vault | ["vault", pool, mint] |
| LP position | ["position", pool, owner] |
| Operator tolls | ["toll", pool, owner, mint] |
| Config and treasury | ["config"] |
Instructions
create_pool, swap, deposit, withdraw, claim_rent, extend_lock, bid, top_up, set_fee, settle, collect_tolls, sweep, buyback.
JavaScript SDK
The SDK is a single ES module that depends only on @solana/web3.js. It builds every instruction and mirrors the program's math in BigInt, so a quote matches the program to the lamport, pending rent included.
import { Connection } from "@solana/web3.js";
import * as tb from "./sdk/tollbooth.js";
const conn = new Connection("https://api.devnet.solana.com");
const pools = await tb.listPools(conn);
const pool = await tb.loadPool(conn, pools[0].address);
const slot = BigInt(await conn.getSlot());
// what a 1 SOL swap pays and gets, exactly as the program computes it
const q = tb.predictSwap(pool.state, slot, 1_000_000_000n, true);
// q.fee, q.lpFee, q.toll, q.out, q.phase ("launch" | "quiet" | "booth")
// bid for the booth: rent per slot, deposit, and the fee you'll charge
const ix = tb.bidIx(pool, wallet, { rent: 1_000_000n, deposit: 2_000_000_000n, feeBps: 120 });
Status and risks
Tollbooth runs on Solana devnet only, with test SOL that has no value. The program has not been audited. As on any AMM, LPs carry price risk, and an operator can lose money if its tolls don't cover its rent. Nothing here is financial advice.