Skip to main content
Pre-Release Documentation

This describes a pre-release version of LBAMM. Interfaces and behavior may change.

Verify the exact repository commit before building production integrations.

Fixed Price Pool

The Fixed Price Pool enforces a constant token ratio while supporting asymmetric liquidity provisioning through a height-based accounting model.

This document explains:

  1. How the liquidity model works
  2. How pools are created
  3. Position lifecycle (add → accrue → collect → withdraw)
  4. Swap behavior
  5. Quoter usage
  6. Event indexing and off-chain height reconstruction

This page is written for integrators interacting with LBAMM Core.


Liquidity Model

Fixed Ratio Pricing

Fixed pools enforce a constant ratio between token0 and token1 with a potential range of ratios from type(uint128).max:1 to 1:type(uint128).max.

The ratio is encoded within fixed pools as:

packedRatio = (ratio0 << 128) | uint128(ratio1);

Example:

  • ratio0 = 1
  • ratio1 = 4
  • Swapping 100 token0 yields 400 token1 (before fees).

During pool creation a supplied ratio will be simplified by the greatest common divisor between the ratio amounts to avoid duplicate pools with the same parameters and effective price ratio. For example - a ratio of 9:3 will be simplified to 3:1.

The ratio is converted into sqrtPriceX96 during creation to integrate with LBAMM’s pricing and hook infrastructure.

The price does not change after deployment (except for rounding dust).


Height-Based Liquidity Accounting

Because price does not move, liquidity attribution is handled through a height system.

Conceptually:

  • Liquidity providers deposit token0 and/or token1.
  • Deposits are represented along height ranges.
  • Swap flow moves height up or down.
  • Positions earn fees while current height is within their range.

A unit of height corresponds to:

1 unit of token × number of active liquidity positions at that height.

If 6 providers each supply 100 tokens across height 0–100:

  • Each height unit represents 6 tokens.
  • A swap of 300 tokens moves height by 50.

This model enables:

  • Fully asymmetric liquidity
  • Deterministic swap execution
  • Fair fee attribution

Creating a Fixed Pool

Pools are created via LBAMM core by calling the createPool function with:

  • PoolCreationDetails.poolType set to the Fixed Pool address.
  • PoolCreationDetails.poolParams set to the ABI encoded bytes of a FixedPoolCreationDetails struct.

FixedPoolCreationDetails

Pool-specific configuration is encoded into poolParams.

struct FixedPoolCreationDetails {
uint8 spacing0;
uint8 spacing1;
uint256 packedRatio;
}

Parameters

spacing0 / spacing1

  • Height granularity for token0 and token1.
  • Liquidity must align to 10^spacing.
  • Prevents griefing via excessive micro-positions.
  • Maximum value: 24.

packedRatio

  • Encodes fixed price ratio.
  • Must represent non-zero ratio.
  • Simplified internally to smallest values that represent the ratio (ie. 9:3 becomes 3:1).

Encoding Pool Creation

Example:

FixedPoolCreationDetails memory fixedDetails = FixedPoolCreationDetails({
spacing0: 4,
spacing1: 4,
packedRatio: (uint256(1) << 128) | uint128(4)
});

PoolCreationDetails memory details = PoolCreationDetails({
poolType: FIXED_POOL_TYPE,
fee: 30, // 0.30%
token0: token0,
token1: token1,
poolHook: address(0),
poolParams: abi.encode(fixedDetails)
});

(bytes32 poolId,,) = lbamm.createPool(
details,
"",
"",
"",
""
);

poolId is deterministic and encodes:

  • pool type
  • fee
  • height spacing
  • parameters such as token pair, pool hook, and packed ratio become a hashed representation within poolId

Position Lifecycle

Positions are created by adding liquidity to the pool. Fixed pools utilize the base position identifier supplied by the LBAMM core without modification, meaning that a single liquidity provider has one effective position per liquidity hook that they deposit liquidity with. Additional deposits to a position or partial withdrawals from a position will reposition the net new position - existing position value plus additional deposit (or minus partial withdrawal) - around the current height.

Adding and removing liquidity will collect all owed fees for the position. Fees may also be collected through the LBAMM collectFees function without modifying liquidity.


Adding Liquidity

Liquidity is added to a fixed pool through the addLiquidity function on LBAMM core. Liquidity may be provided to either or both tokens in the pool in any amounts that align with the pool's defined token height spacing.

Pool-specific parameters are encoded into:

struct FixedLiquidityModificationParams {
uint256 amount0;
uint256 amount1;
bool addInRange0;
bool addInRange1;
uint256 endHeightInsertionHint0;
uint256 endHeightInsertionHint1;
uint256 maxStartHeight0;
uint256 maxStartHeight1;
}

Parameter Details

amount0 / amount1

  • Maximum deposit amounts.
  • Actual deposits determined by height alignment.

addInRange0 / addInRange1

  • Allows deposit inside current height.
  • Consumes portion of opposite token to align.
    • For example:
      • A pool with a ratio of 1 token0 to 3 token1 has a height spacing of 100 for token0, token0 current height is 10.
      • If addInRange0 is false, the liquidity for token0 will deposit at a start height of 100.
      • If addInRange0 is true, the liquidity for token0 will deposit at a start height of 0 and 30 token1 will be removed from amount1.

endHeightInsertionHint0 / endHeightInsertionHint1

  • Fixed pools maintain a linked list of heights for each token in the pool to efficiently traverse the heights.
  • Start heights are simple to insert into the linked list as the nodes above and below the current height are known.
  • End heights depend on the start height and amount of liquidity being added which could place them anywhere above the current height in the linked list.
  • Insertion hints optimize the search for the insertion point in the linked list by giving the fixed pool a starting point for the search.
  • Well defined hints are nodes that are nearby the expected end height of the liquidity position.
  • Poorly defined hints may result in extra search costs as the pool searches for the correct node to insert at.

Integrators should monitor active heights via events to determine optimal end height insertion hints.

maxStartHeight0 / maxStartHeight1

  • Front-running protection.
  • Reverts if computed start height exceeds bound.
  • Prevents forced out-of-range liquidity.

Example Add Liquidity

FixedLiquidityModificationParams memory fixedParams = FixedLiquidityModificationParams({
amount0: 100e18,
amount1: 0,
addInRange0: false,
addInRange1: false,
endHeightInsertionHint0: knownHeight0,
endHeightInsertionHint1: 0,
maxStartHeight0: currentHeight0 + 10,
maxStartHeight1: 0
});

LiquidityModificationParams memory params = LiquidityModificationParams({
liquidityHook: liquidityHook,
poolId: poolId,
minLiquidityAmount0: 100e18,
minLiquidityAmount1: 0,
maxLiquidityAmount1: 110e18,
maxLiquidityAmount1: 0,
maxHookFee0: 1e17,
maxHookFee1: 0,
poolParams: abi.encode(fixedParams)
});

lbamm.addLiquidity(params, hooksExtraData);

Collecting Fees

Fees may be collected through the LBAMM core collectFees function without modifying a liquidity position. Since positions in the fixed pool are determined by the provider and liquidity hook there are no pool parameters required for determining the position and poolParams in the LiquidityCollectFeesParams struct may be left as empty bytes.

Fees accrue while the current height of the token where the provider has supplied liquidity moves between the position's start and end height.

Collection:

  • Updates fee checkpoints
  • Reduces pool fee balances
  • Transfers fees to provider

Example Collect Fees

LiquidityCollectFeesParams memory params = LiquidityCollectFeesParams({
liquidityHook: liquidityHook,
poolId: poolId,
maxHookFee0: 1e17,
maxHookFee1: 0,
poolParams: bytes("")
});

lbamm.collectFees(params, hooksExtraData);

Removing Liquidity

Liquidity is removed from a fixed pool through the removeLiquidity function on LBAMM core. Liquidity may be partially or fully removed from a position.

When liquidity is to be partially removed the LiquidityModificationParams.poolParams value is set to ABI encoded bytes of FixedLiquidityWithdrawalParams with withdrawAll set to false and params set to ABI encoded bytes of FixedLiquidityModificationParams. The amount0 and amount1 fields are the intended withdrawal amounts and the remaining fields are populated in the same manner as adding liquidity. Fixed Pool will compute the value of the position, deduct the intended withdrawal amounts and apply deposit logic to the remaining amounts. If there is any value that cannot be redeposited the withdraw amounts will increase over intended.

When liquidity is to be fully removed the LiquidityModificationParams.poolParams value is set to ABI encoded bytes of FixedLiquidityWithdrawalParams with withdrawAll set to true and params set to ABI encoded bytes of FixedLiquidityWithdrawAllParams with minAmount0 and minAmount1 providing front-run protection against swaps that change the expected withdrawal amount beyond provider intent without confirmation.

struct FixedLiquidityWithdrawalParams {
bool withdrawAll;
bytes params;
}

Example Remove Liquidity (Partial)

FixedLiquidityModificationParams memory fixedParams = FixedLiquidityModificationParams({
amount0: 100e18,
amount1: 0,
addInRange0: false,
addInRange1: false,
endHeightInsertionHint0: 20,
endHeightInsertionHint1: 0,
maxStartHeight0: currentHeight0 + 10,
maxStartHeight1: 0
});

FixedLiquidityWithdrawalParams memory withdrawParams = FixedLiquidityWithdrawalParams({
withdrawAll: false,
params: abi.encode(fixedParams)
});

LiquidityModificationParams memory params = LiquidityModificationParams({
liquidityHook: liquidityHook,
poolId: poolId,
minLiquidityAmount0: 100e18,
minLiquidityAmount1: 0,
maxLiquidityAmount1: 110e18,
maxLiquidityAmount1: 0,
maxHookFee0: 1e17,
maxHookFee1: 0,
poolParams: abi.encode(withdrawParams)
});

lbamm.removeLiquidity(params, hooksExtraData);

Example Remove Liquidity (Full)

FixedLiquidityWithdrawAllParams memory fixedParams = FixedLiquidityWithdrawAllParams({
minAmount0: 100e18,
minAmount1: 0
});

FixedLiquidityWithdrawalParams memory withdrawParams = FixedLiquidityWithdrawalParams({
withdrawAll: true,
params: abi.encode(fixedParams)
});

LiquidityModificationParams memory params = LiquidityModificationParams({
liquidityHook: liquidityHook,
poolId: poolId,
minLiquidityAmount0: 100e18,
minLiquidityAmount1: 0,
maxLiquidityAmount1: 110e18,
maxLiquidityAmount1: 0,
maxHookFee0: 1e17,
maxHookFee1: 0,
poolParams: abi.encode(withdrawParams)
});

lbamm.removeLiquidity(params, hooksExtraData);

Swap Behavior

Swaps execute at fixed ratio. Height moves instead of price.

Input-Based Swap

  • Consumes up to amountIn
  • May partially fill if insufficient output reserves
  • Returns actual input consumed

Output-Based Swap

  • Requests exact output
  • Input rounded up to meet ratio
  • May generate dust

Example (4:1 pool):

  • Request 9 token1
  • Requires 3 token0
  • 3 token0 produces 12 token1
  • 3 token1 becomes dust

Dust is internally accounted.

Swapping near liquidity exhaustion may revert.


Quoter

Fixed pools expose a companion FixedPoolQuoter contract that provides read-only calculations over the current FixedPoolType state (position ranges, current heights, fee growth checkpoints, etc.) without requiring the pool type itself to surface every convenience view.

The quoter uses a static delegatecall pattern to execute logic against the FixedPoolType storage layout while preventing state mutation. This keeps the pool type focused on execution logic while still enabling rich UX and integration tooling.

Quote the value required for an in-range add

When adding liquidity “in range”, the pool may require supplying some amount of the opposite token to cover the depth between the current height and the next spacing boundary. This helper returns the approximate amounts needed to perform an in-range add at the pool’s current height.

(uint256 amount0, uint256 amount1) =
fixedPoolQuoter.quoteValueRequiredForInRangeAdd(poolId);
  • amount0: token0 required to add token1 liquidity in-range
  • amount1: token1 required to add token0 liquidity in-range

This is primarily a UX / routing primitive. It allows an integrator to:

  • determine whether the user already holds enough of the opposite token
  • pre-calculate required swap amounts before liquidity provisioning
  • preview capital efficiency before submitting a transaction

Quote position principal value and accrued fees

This helper computes both principal value and fees for a given position, mirroring the pool type’s internal valuation logic without modifying fee checkpoints.

(uint256 value0, uint256 value1, uint256 fee0, uint256 fee1) =
fixedPoolQuoter.quotePositionValue(poolId, positionId);
  • value0, value1: current principal value attributed to the position (token0/token1)
  • fee0, fee1: currently accrued, uncollected fees (token0/token1)

This is the primary primitive for:

  • position dashboards and portfolio valuation
  • estimating withdraw or collect outcomes before submitting a transaction
  • monitoring liquidity health and behavior near exhaustion boundaries
  • building analytics around height traversal and fee distribution

Events & Indexing

Fixed pools emit:

event FixedPoolPositionUpdated(
bytes32 indexed poolId,
bytes32 indexed positionId,
uint256 startHeight0,
uint256 endHeight0,
uint256 startHeight1,
uint256 endHeight1
);

event FixedPoolHeightConsumed(
bytes32 indexed poolId,
bool indexed zeroForOne,
uint256 currentHeight0,
uint256 currentHeight1
);

Off-Chain Height Reconstruction

To maintain height state off-chain:

  1. Track FixedPoolPositionUpdated
  2. Maintain active height intervals
  3. Track FixedPoolHeightConsumed
  4. Update current height
  5. Derive in-range liquidity

Insertion hints should reference known active heights from this reconstructed state.

Limit Break

TwitterLimitBreak.comMedium

© 2026 Limit Break International, Inc. All rights reserved.

Privacy PolicyTerms of ServiceCookie PolicyDo Not Sell My Info