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.

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, and collectFees revert unless msg.sender is 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 sqrtPriceX96 and 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 poolId derivation so multiple pools can exist with the same (token0, token1, fee, hook) tuple.
  • sqrtPriceRatioX96
    • Initial price value stored by the pool type.
    • Returned by getCurrentPriceX96(...) until the first swap occurs.
    • Not included in the poolId derivation.

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.sender as 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, amount1 are exact amounts.
    • For addLiquidity: exact deposits into the pool.
    • For removeLiquidity: exact withdrawals from the pool.
  • 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 amountOut using 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 sqrtPriceX96 returned 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
);
  • sqrtPriceX96 is the execution price used for the swap.
  • liquidity is 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.

Limit Break

TwitterLimitBreak.comMedium

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

Privacy PolicyTerms of ServiceCookie PolicyDo Not Sell My Info