Security
A module runs inside the swap, but it is bounded by it. What a module can and cannot do, the checks the AMM makes on every call, and the risks that remain.
What a module cannot do#
- Touch the vaults
- Vault authority is the pool PDA, and the AMM never passes it to a module as a signer. The hook authority it does sign with owns nothing.
- Use your signature
- Every account forwarded to a module has is_signer = false. The user, the payer and the pool never sign for a module.
- Pose as another module
- Each module of a pool has its own hook authority, ["hook_auth", pool, module], and the AMM signs for exactly one per call.
- Re-enter the pool
- Solana forbids A → B → A calls, so a module cannot call back into the AMM in the middle of a swap.
- Break min-out
- The user receives at least min_amount_out, or the whole transaction reverts.
- Change the rules later
- The patch is part of the pool’s address and can never change. Built-in module params are fixed at creation.
- Exceed its jacks
- The AMM calls only the hook points whose bits are set, and ignores fee overrides and deltas from modules without the matching bit.
What a module can do#
- Revert
- Any module can make the instruction it is called in fail. A module with Before remove can refuse withdrawals: that is how Lockup works, and it is also what a hostile module would do.
- Raise the fee
- With Dynamic fee, up to 50% for that swap.
- Trade next to you
- With Returns delta, take part of the remaining input before the curve, or run its own curve trades after it. It must deposit first; the user is protected by min_amount_out.
- Read the swap
- Sender, direction, amounts, reserves, fee and timestamp are passed to it.
- Spend compute
- Its calls count toward the transaction’s compute budget.
Checks on every module call#
| Check | What it prevents |
|---|---|
| slice[0], slice[1], slice[2] equal the program, module and hook authority stored in the pool | The caller cannot swap in a different program or authority. |
| Signs only with the ["hook_auth", pool, module] seeds | The module gets a signature that is good for itself and nothing else. |
| is_signer = false on every forwarded account | No user, payer or pool signature ever reaches a module. |
| Return data used only if the module program set it; malformed data fails | Another program cannot answer on a module’s behalf. |
| Fee override at most 5000 bps, and only with Dynamic fee | The fee has a hard ceiling of 50%. |
| take_in at most the remaining input, recipient index ≥ 3 | A module takes only what it reported, into its own accounts. |
| give_out and trade inputs checked by vault balance difference | A module is paid only for tokens it actually deposited. |
| No vault balance may decrease during a module call | Nothing leaves the vaults except through the AMM’s own transfers. |
| At most 4 hook trades, each with out ≥ min_out | Bounded work, no surprise prices. |
| vault ≥ reserve + protocol fees, both sides, after every instruction | Accounting can never claim more than the vaults hold. |
| Checked u128 math, rounding in the pool’s favour | No overflow, no rounding drain. |
Checks at pool creation#
- At most four modules, all distinct.
- Each module account is owned by its program, the program is executable, and the module account is not itself a program.
- The Patchbay program cannot be a module program.
- Every module’s flags are valid.
fee_bpsis at most 1000; the mints are different and ordered.- Token-2022 mints carry only allowed extensions: no transfer fees, transfer hooks, permanent delegates or other extensions that change how transfers behave. The check runs again on the first deposit.
What the admin can and cannot do#
One admin key manages the global config. initialize_config can only be signed by the AMM program’s upgrade authority, so nobody can claim the admin role between deployment and setup. The admin can:
- set the treasury and the protocol’s share of the LP fee, at most 25%;
- list or unlist modules in the registry;
- send a pool’s accrued protocol fees to the treasury;
- hand the admin role to another key, in two steps (propose, then accept).
It cannot change a pool’s fee or patch, move reserves or LP funds, or pause swaps. The registry gates nothing on-chain: it only decides which modules the app shows by name.
Risks#
For liquidity providers#
- A pool is only as safe as its modules. Read every module in the patch before you deposit: a Before remove module can keep you in, and a Returns delta module trades against the pool.
- A Lockup locks the whole pool until its unlock time, including liquidity added later.
- Impermanent loss, as in any constant-product pool.
For traders#
- Always set a minimum output. The app and the SDK do; it is your protection against a fee change or a hostile module.
- A dynamic fee can change between your quote and your transaction.
For everyone#
- Patchbay v1 is new software. No audit report is published yet; this page will link one when it is.
- A listing in the registry is not an audit.