fee for a swap at 14:20
Base 0.30%, +0.5 bps of fee per bps of average move, capped at 1.50%. The average halves every 10 minutes.
Patchbay is a constant-product AMM on Solana. Every pool runs an ordered chain of up to four hook modules — dynamic fee, TWAP oracle, limit order, lockup, or your own program — around each swap and liquidity change. A module’s permissions are the last byte of its address.
0x43Raises the fee after big moves.
0x82Fills resting orders after swaps.
Before swap
Dynamic fee
Curve
SOL 150.00 → 149.25
After swap
Dynamic fee → Limit order
10 SOL
you send
USDC
you receive · example pool
2 of 4 slots patched. Fee 0.42%. 10 SOL in, 1,489.99 USDC out.
A swap is one instruction. The pool key picks the pool, modules run in patch order around the curve, and you are paid last. Scroll through it, or switch the patch.
01 / 05
A pool is the address derived from its key. Same mints, fee and patch give the same pool; change the order of the modules and it is a different pool. The patch can’t be changed after creation.
A module’s permissions are the last byte of its account address. Module programs mine that address — about 256 tries on average — so anyone can read what a module may do from the address alone. Toggle the jacks.
Your module
Same flags as the built-in Dynamic fee
0100 0011=0x43
Ready
PDA of patchbay-modules · seeds “module” + nonce · what create_module needs for Dynamic fee
0tries
—
last byte0x43
Or load a built-in: , , ,
One example pool patched with all four built-ins, run through six hours of simulated swaps under the same rules the programs use. Drag the clock.
14:20
fee for a swap at 14:20
Base 0.30%, +0.5 bps of fee per bps of average move, capped at 1.50%. The average halves every 10 minutes.
30-minute TWAP · spot 146.07
Records the price before each swap. Any program can call consult(1800) for the 30-minute mean, so a brief spike moves it only a little.
13:42 fill · 8.10 SOL at 145.71 · 1,180 of 2,920 USDC spent
A bid for 20 SOL at ≤ 146.00, placed at 12:00: 2,920 USDC in escrow. When a swap pushes the price, fee included, under the limit, after_swap fills it until the price is back at the limit.
until 16:00 · remove liquidity reverts
at 14:20
Pool created at 12:00 with a 4-hour Lockup. Until 16:00 nobody can remove liquidity, whenever they added it. Lockup only has a before-remove jack: swaps never call it.
A module can run at the hook points its address allows, set the fee (up to 50%), take part of the input, return its own trades or refuse a liquidity change. That is all it gets.
No keys to the vaults
Vaults belong to the pool. Each module is called with its own hook authority, a PDA that owns nothing, and vault balances can only grow while a module runs.
No forwarded signatures
Your signature, and the payer’s, never reach a module. The only signer it sees is its own hook authority.
No re-entry
Solana forbids A → B → A calls, so a module can’t call back into the AMM in the middle of your swap.
All or nothing
If any module fails, the whole transaction reverts. Nothing moves.
Your minimum holds
Min out is checked after every module has run, right before you are paid.
When a module fails
Patched pool
Dynamic fee→Limit order
1,489.99 USDC
Another pool
plain · 0.30% · smaller
1,488.08 USDC
Simulation passes. The router picks the better quote: 1,489.99 USDC through the patched pool.
Example pools. Routing happens in the app before you sign; on-chain, a failed module only ever reverts.