This describes a pre-release version of LBAMM. Interfaces and behavior may change.
Verify the exact repository commit before building production integrations.
Fee Surfaces
This page enumerates every fee surface in LBAMM and defines, for each surface:
- Trigger (when it can be charged)
- Who defines it
- Denomination (which token the fee is paid in)
- Recipient
- Bounds
- Settlement semantics
- Observability (events / query surfaces)
This page is reference-oriented and describes protocol behavior (not recommended strategies).
Summary table
Terminology: “Execution-level” means values passed to the top-level swap call (applies once per
singleSwap/multiSwap, not per hop). “Hop-level” means per-pool-hop behavior inside a multi-hop route.
| Surface | Scope | Who defines it | Denomination | Recipient | Bounds / validation | Settlement | Observable |
|---|---|---|---|---|---|---|---|
| Exchange fee | Swap (execution-level) | Caller-supplied params (BPSFeeWithRecipient) | tokenIn | exchangeFee.recipient | Swap-by-input: BPS <= 10_000; swap-by-output: BPS < 10_000; if recipient==0 then BPS==0 | Core transfers fee to recipient; handler (if used) supplies total tokenIn incl. fees | Not in Swap event today |
| Fee-on-top | Swap (execution-level) | Caller-supplied params (FlatFeeWithRecipient) | tokenIn | feeOnTop.recipient | Swap-by-input: amount <= amountSpecified; swap-by-output: no protocol cap; if recipient==0 then amount==0 | Core transfers fee to recipient; handler (if used) supplies total tokenIn incl. fees | Not in Swap event today |
| Executor fee | Swap (execution-level) | Implemented as fee-on-top | tokenIn | Executor or designated recipient | Same as fee-on-top | Same as fee-on-top | Same as fee-on-top |
| LP fee | Swap (hop-level / pool-level) | Pool configuration (fixed) or pool hook (dynamic selection) | Hop’s effective input token for that hop | Liquidity providers (pool accounting) | Core-enforced caps on dynamic selection by swap mode (see pool hook docs) | Accounted inside pool; shown as lpFeeAmount in Swap event | Swap event includes lpFeeAmount |
| Token-hook fees (swap) | Swap (per hop; before/after) | Token hook return values | Per token hook rules (see Token Hooks) | Accrued to hook or attributed per token-hook settings | No explicit “max” param; cannot overconsume between both hooks; excessive fees can cause swap limit checks to revert | Accrued in core; collectible by hook; hooks may queue collection depending on settings | Not in Swap event today |
| Token-hook fees (liquidity) | Liquidity ops | Token hook return values | token0 / token1 | Accrued to hook or attributed per token-hook settings | Subject to maxHookFee0/1 (total across all hooks) | Accrued in core; may be queued for collection post-op | Reflected indirectly via liquidity events’ fees0/fees1 fields (see notes) |
| Pool-hook fees (liquidity) | Liquidity ops | Pool hook return values | token0 / token1 | Accrued to pool hook address | Subject to maxHookFee0/1 (total across all hooks) | Accrued in core; collectible by hook | Reflected indirectly via liquidity events’ fees0/fees1 fields (see notes) |
| Position-hook fees (liquidity) | Liquidity ops | Position hook return values | token0 / token1 | Accrued to position hook address | Subject to maxHookFee0/1 (total across all hooks) | Accrued in core; collectible by hook | Reflected indirectly via liquidity events’ fees0/fees1 fields (see notes) |
| Protocol fees | Multiple (configurable) | Protocol configuration (global + overrides + per-hop minimums) | Depends on surface (can be taken from hop token / from other fee surfaces) | Protocol | Queryable fee rates; emitted when taken | Accounted internally; emitted when taken | ProtocolFeeTaken(token, amount) event |
| Flashloan fee | Flashloan | Loan token’s flashloan hook | feeToken returned by hook (may differ from loan token) | Stored like token hook fees (collectible under same model) | No explicit cap in core; feeToken must allow use (via validation) | Accrued in core (token-hook fee model) | Flashloan event includes feeToken + feeAmount |
Execution-level swap fees
Execution-level fees are provided to singleSwap / multiSwap and apply once per swap call (even for multi-hop routes).
Exchange fee (BPS, proportional)
What it is
A percentage fee (basis points) applied to the swap’s execution-level input.
Where it’s encoded
struct BPSFeeWithRecipient {
uint16 BPS;
address recipient;
}
Bounds / validity
- Swap-by-input:
exchangeFee.BPS <= 10_000 - Swap-by-output:
exchangeFee.BPS < 10_000 - If
exchangeFee.recipient == address(0), thenexchangeFee.BPSmust be 0
Multi-hop
- Applied once to the execution-level input, not per hop.
Fee-on-top (flat amount, additive)
What it is A flat fee amount denominated in the swap’s input token.
Where it’s encoded
struct FlatFeeWithRecipient {
uint256 amount;
address recipient;
}
Bounds / validity
- Swap-by-input:
feeOnTop.amount <= amountSpecified - Swap-by-output: no protocol-level cap on
feeOnTop.amount - If
feeOnTop.recipient == address(0), thenfeeOnTop.amountmust be 0
Multi-hop
- Applied once to the execution-level input, not per hop.
Ordering between exchange fee and fee-on-top
Ordering differs by swap mode.
Swap-by-input
- Fee-on-top is removed from input before exchange fee is applied.
- Exchange fee is computed from the remaining input.
Swap-by-output
- Exchange fee scaling is applied first (input increases to cover BPS fee).
- Fee-on-top is added after exchange fee.
This ordering is strictly about execution-level fee adjustments and should not be confused with per-hop pool mechanics.
Executor fees
Executor fees are implemented as fee-on-top.
- If an executor is compensated at swap-time, that compensation is explicitly encoded as
feeOnTop. - There is no hidden solver reward surface.
Example: setting execution-level fees in a swap call
// Execution-level swap fees:
BPSFeeWithRecipient memory exchangeFee = BPSFeeWithRecipient({
BPS: 30, // 0.30%
recipient: exchangeFeeRecipient
});
FlatFeeWithRecipient memory feeOnTop = FlatFeeWithRecipient({
amount: 1e15, // flat fee in tokenIn units
recipient: executor // executor fee
});
// Validity constraints:
require(exchangeFee.recipient != address(0) || exchangeFee.BPS == 0, "invalid exchange fee");
require(feeOnTop.recipient != address(0) || feeOnTop.amount == 0, "invalid fee-on-top");
// For swap-by-input, feeOnTop cannot exceed the specified input amount (pseudo-check).
// (Exact check depends on how swap mode is expressed in your SwapOrder.)
Pool-level fees
LP fee
What it is The fee charged by a pool’s liquidity model and distributed to liquidity providers.
Where it’s defined
- Fixed in pool configuration, or
- Dynamically selected by a pool hook when the pool is created with the dynamic-fee sentinel.
Denomination
- Charged in the hop’s effective input token (hop-level).
Observability
- LP fee amount for a hop emitted during swap:
event Swap(
bytes32 indexed poolId,
address indexed recipient,
bool zeroForOne,
uint256 amountIn,
uint256 amountOut,
uint256 lpFeeAmount
);
- Liquidity events include fee fields for amount collected by a position:
event LiquidityAdded(bytes32 indexed poolId, address indexed provider, uint256 amount0, uint256 amount1, uint256 fees0, uint256 fees1);
event LiquidityRemoved(bytes32 indexed poolId, address indexed provider, uint256 amount0, uint256 amount1, uint256 fees0, uint256 fees1);
event FeesCollected(bytes32 indexed poolId, address indexed provider, uint256 fees0, uint256 fees1);
LP fee is a pool-level mechanism. It is not an execution-level fee adjustment and should not be described as “before/after exchange fee” at the route level.
Hook-level fees
Hook-level fees are assessed by hooks (token/pool/position) and are accrued in core for later collection. Hooks may be able to queue collection so that distribution occurs post-execution, depending on settings (documented in the Hooks section).
Token-hook fees on swaps
Trigger
- Token hook
beforeSwapand/orafterSwap(per hop; gated by token settings)
Bounds
- No explicit max parameter for swap hook fees.
- Fees cannot overconsume the trade between both hooks.
- Excessive hook fees can cause limit validation to revert (swap’s limit constraints).
Settlement
- Accrued to a balance in core; collectible by hook.
- Some configurations allow attribution to the token rather than the hook address (token hook settings).
Fee denomination truth tables are defined in the Token Hooks docs; this page treats token swap hook fees as a distinct surface.
Hook fees on liquidity operations (token + position + pool)
For liquidity operations, multiple hooks may assess fees:
- Token hooks (token0/token1 sides)
- Position hook (liquidity hook)
- Pool hook
Denomination
- Always
token0/token1of the pool for liquidity operations.
Bounding
maxHookFee0/maxHookFee1are bounds on the total hook fees (summed across hooks) per token.- Hooks do not coordinate; each returns fees independently.
- Enforcement compares the final totals to the max bounds.
Netting semantics
- Hook fees can increase a provider’s required deposit or reduce their withdrawal.
- In some cases, withdrawal may be reduced enough that the provider must supply additional funds.
Settlement
- Accrued in core, collectible by the hook(s).
- Hooks may queue collection during execution; settlement occurs after the primary operation finalizes.
(If you want a stricter definition per event field, we can add it once you confirm the intended semantics.)
Protocol fees
LBAMM supports protocol fee configurations at multiple levels, including:
-
Minimum protocol fees on specific token hops (configured individually; default none)
-
Global protocol fees on:
- LP fees
- Exchange fees
- Fee-on-top fees (defaults 0)
-
Overrides for specific:
- poolId (LP fee protocol fee)
- exchange fee recipient
- fee-on-top recipient
Because protocol fee utilization may be defaulted to zero initially and may evolve, this page treats protocol fees as a distinct surface at a high level.
Flashloan fees
Flashloan fees are defined by the loan token’s flashloan hook, which returns:
feeToken(may differ from the loan token)feeAmount
If feeToken differs, the fee token's hook must allow the loan token to use it as a fee currency (via validation).
Settlement
- Stored and collected using the same accounting model as token hook fees.
Observability
event Flashloan(
address indexed requester,
address indexed executor,
address indexed loanedToken,
uint256 loanAmount,
address feeToken,
uint256 feeAmount
);
Related pages
- Fee Ordering & Settlement — deterministic ordering rules and settlement semantics across operations
- Token Hooks / Pool Hooks / Position Hooks — hook fee denomination, flags, queueing, and collection mechanics
- Pool Types — LP fee behavior and pool-specific math
