This describes a pre-release version of LBAMM. Interfaces and behavior may change.
Verify the exact repository commit before building production integrations.
Single Provider Pool
A Single Provider Pool is a pool type where one address owns and controls all liquidity for a given poolId.
This pool type is designed for markets where:
- liquidity ownership must be unambiguous (one provider),
- liquidity operations must be tightly permissioned (provider-only),
- swap pricing is supplied by a hook (policy-defined pricing).
A non-zero pool hook is required. The hook is responsible for:
- providing the execution price on every swap, and
- defining (and returning) the allowed liquidity provider for the pool.
Key Guarantees for Integrators
You can rely on the following:
- Provider-only liquidity operations:
addLiquidity,removeLiquidity, andcollectFeesrevert unlessmsg.senderis the hook-defined provider for the pool. - Position identity is trivial:
positionId = poolId(no per-provider / per-range positions). - Reserves are the source of truth: the pool type uses LBAMM core reserves (
reserve0,reserve1) to determine available liquidity. - Swap execution price is hook-defined per swap: the pool type asks the hook for a
sqrtPriceX96and executes at that price. - Partial fills are supported: if the pool cannot satisfy the computed output amount, it caps output to available reserves and adjusts input accordingly.
Pool Creation Parameters
Single Provider Pools use the following poolParams when calling createPool on LBAMM core:
/**
* @dev Parameters for creating a single-provider pool.
*
* @dev **salt**: Unique salt used to ensure deterministic pool deployment address.
* @dev **sqrtPriceRatioX96**: Initial price ratio as sqrt(price) * 2^96
*/
struct SingleProviderPoolCreationDetails {
bytes32 salt;
uint160 sqrtPriceRatioX96;
}
Field semantics
salt- Arbitrary
bytes32. - Included in the
poolIdderivation so multiple pools can exist with the same(token0, token1, fee, hook)tuple.
- Arbitrary
sqrtPriceRatioX96- Initial price value stored by the pool type.
- Returned by
getCurrentPriceX96(...)until the first swap occurs. - Not included in the
poolIdderivation.
Pool identity
The pool type derives a deterministic poolId from the pool type address, fee, salt, token pair, and poolHook. (The creation-time price is intentionally excluded.)
Pool Hook Requirements
Single Provider Pools must be created with a non-zero poolHook. A zero address hook causes pool creation to revert.
The hook must implement ISingleProviderPoolHook:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "@limitbreak/lb-amm-core/src/interfaces/hooks/ILimitBreakAMMPoolHook.sol";
/**
* @notice Interface definition for a single provider pool's hook to get price and provider.
*/
interface ISingleProviderPoolHook is ILimitBreakAMMPoolHook {
struct HookPoolPriceParams {
bool inputSwap;
bytes32 poolId;
address tokenIn;
address tokenOut;
uint256 amount;
}
/**
* @notice Executed during a swap to return the price to execute at.
*/
function getPoolPriceForSwap(
SwapContext calldata context,
HookPoolPriceParams calldata poolPriceParams,
bytes calldata hookData
) external returns (uint160 poolPrice);
/**
* @notice Returns the single allowed liquidity provider for the pool.
*/
function getPoolLiquidityProvider(
bytes32 poolId
) external view returns (address provider);
}
What integrators should assume about the provider
- The allowed provider is defined by the hook and is pool-specific.
- LBAMM core uses
msg.senderas the liquidity operation “provider” identity for these checks. - If you insert an external router, the router becomes the provider for hook purposes (and will fail unless it is the allowed provider).
How the hook decides or initializes the provider is intentionally not specified by the pool type. It may be set during pool creation via hook data, derived from internal state, or computed by custom logic.
Liquidity Modification Parameters
Single Provider Pools use the following poolParams when calling addLiquidity and removeLiquidity on LBAMM core:
/**
* @notice Parameters for modifying single-provider pool liquidity.
* @dev Used when adding or removing liquidity from single-provider pools.
*
* @dev **amount0**: Exact amount of token0 to add or remove.
* @dev **amount1**: Exact amount of token1 to add or remove.
*/
struct SingleProviderLiquidityModificationParams {
uint256 amount0;
uint256 amount1;
}
Field semantics
amount0,amount1are exact amounts.- For
addLiquidity: exact deposits into the pool. - For
removeLiquidity: exact withdrawals from the pool.
- For
- Either side may be set to zero (single-sided add/remove is allowed), subject to:
- token hooks,
- the pool hook,
- and available reserves for withdrawals.
Reserves and Fees
Single Provider Pools do not maintain separate pool-type liquidity state. They use LBAMM core PoolState for:
reserve0,reserve1(swapable reserves)feeBalance0,feeBalance1(accrued LP fees)
All LP fees accrue to the single provider.
If the pool is configured with the dynamic fee sentinel at creation time, the pool hook may provide dynamic LP fees. Otherwise, the pool uses a fixed LP fee.
Swap Pricing
On each swap, the pool calls the pool hook's getPoolPriceForSwap function to determine the price to execute the swap at.
Fixed-Price Math Used Per Swap
Single Provider Pools execute swaps at a hook-supplied sqrtPriceX96, defined as:
sqrt(token1 / token0) * 2^96
This value fully determines the execution price for the swap.
Input-Based Swaps
For input-based swaps:
- The trader specifies
amountIn. - The pool computes
amountOutusing the hook-supplied price. - Output amounts are rounded down.
If the output amount exceeds the pool's reserve of the output token, the pool will attempt to fill the swap as an output-based swap with the reserve amount as the output.
Output-Based Swaps
For output-based swaps:
- The trader specifies
amountOut. - The pool computes the required
amountIn. - Input amounts are rounded up.
Rounding up ensures the pool never undercharges input.
Current Price Read Surface
Single Provider Pools expose a “last execution price” read surface:
getCurrentPriceX96(address amm, bytes32 poolId)
Semantics:
- Before any swap occurs: returns the creation-time
sqrtPriceRatioX96. - After swaps: returns the exact
sqrtPriceX96returned by the hook for the most recent swap.
Swap Event
Single Provider Pools emit an additional swap details event:
/// @dev Event emitted when a swap is executed in a single-provider pool.
event SingleProviderPoolSwapDetails(
bytes32 indexed poolId,
uint256 sqrtPriceX96,
uint256 liquidity
);
sqrtPriceX96is the execution price used for the swap.liquidityis the output reserve before the swap (as currently defined).
Indexers can treat LBAMM core reserve and fee balance updates as authoritative state.
Examples
The following examples show typical construction of poolParams for this pool type. These snippets intentionally omit unrelated fields (token approvals, hookData layout, and token hook configuration).
Create a Single Provider Pool
SingleProviderPoolCreationDetails memory sp = SingleProviderPoolCreationDetails({
salt: bytes32(uint256(1)),
sqrtPriceRatioX96: initialSqrtPriceX96
});
bytes memory poolParams = abi.encode(sp);
amm.createPool(
PoolCreationDetails({
poolType: singleProviderPoolType,
fee: feeBps,
token0: token0,
token1: token1,
poolHook: poolHook, // MUST be non-zero
poolParams: poolParams,
}),
token0HookData, // if needed by token0 hook
token1HookData, // if needed by token1 hook
poolHookData, // hook-defined (may initialize provider)
liquidityData // optional initial deposit
);
Add Liquidity (provider-only)
SingleProviderLiquidityModificationParams memory singleParams = SingleProviderLiquidityModificationParams({
amount0: 100e18,
amount1: 0
});
LiquidityModificationParams memory params = LiquidityModificationParams({
liquidityHook: address(0), // position hooks not used; pool hook enforces constraints
poolId: poolId,
minLiquidityAmount0: 100e18,
minLiquidityAmount1: 0,
maxLiquidityAmount1: 100e18,
maxLiquidityAmount1: 0,
maxHookFee0: 1e17,
maxHookFee1: 0,
poolParams: abi.encode(singleParams)
});
lbamm.addLiquidity(params, hooksExtraData);
Remove Liquidity (provider-only)
SingleProviderLiquidityModificationParams memory singleParams = SingleProviderLiquidityModificationParams({
amount0: 90e18,
amount1: 10e18
});
LiquidityModificationParams memory params = LiquidityModificationParams({
liquidityHook: address(0), // position hooks not used; pool hook enforces constraints
poolId: poolId,
minLiquidityAmount0: 85e18,
minLiquidityAmount1: 5e18,
maxLiquidityAmount1: 95e18,
maxLiquidityAmount1: 15e18,
maxHookFee0: 1e17,
maxHookFee1: 0,
poolParams: abi.encode(singleParams)
});
lbamm.addLiquidity(params, hooksExtraData);
amm.removeLiquidity(params, hooksExtraData);
Collect Fees (provider-only)
LiquidityCollectFeesParams memory params = LiquidityCollectFeesParams({
liquidityHook: address(0), // position hooks not used; pool hook enforces constraints
poolId: poolId,
maxHookFee0: 1e17,
maxHookFee1: 0,
poolParams: bytes("")
});
lbamm.collectFees(params, hooksExtraData);
Summary
A Single Provider Pool has:
- provider-only liquidity operations enforced via the pool type with the pool hook supplying the allowed provider address,
- hook-defined per-swap pricing (
sqrtPriceX96), - core-reserve-based liquidity availability, and
- partial fill behavior when reserves are insufficient
The pool hook is the policy surface: it defines who controls liquidity and what price swaps execute at.
