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.

Create a Pool

This guide walks through creating a new pool in LBAMM.

It assumes:

  • Tokens are already deployed.
  • You understand basic pool concepts.
  • You are interacting with a production deployment.

Creating a pool consists of:

  1. Preparing PoolCreationDetails
  2. Optionally preparing hook data
  3. Calling createPool
  4. Handling deterministic poolId
  5. (Optional) Adding liquidity during creation

Pool Identity Is Deterministic

Pools in LBAMM are identified by a deterministic poolId.

poolId is generated by the pool type contract, subject to constraints enforced by LBAMM core.

Pool types must implement:

function computePoolId(
PoolCreationDetails calldata poolCreationDetails
) external view returns (bytes32 poolId);

poolId layout:

Bits   0 to  15  - LP fee in BPS
Bits 16 to 143 - creation details hash (may include pool-specific params)
Bits 144 to 255 - pool type address (must fit in 112 bits)

Important

  • poolId can be computed offchain using computePoolId.
  • The same PoolCreationDetails always produces the same poolId.
  • LBAMM prevents initializing the same poolId twice.
  • Pool creation is permissionless unless restricted by hooks or custom pool type logic.

PoolCreationDetails

struct PoolCreationDetails {
address poolType;
uint16 fee;
address token0;
address token1;
address poolHook;
bytes poolParams;
}

Field Semantics

  • poolType Address of the pool type module.

  • fee LP fee in BPS (max 10000 = 100%), OR sentinel value of 55555 for dynamic fee.

  • token0 / token1 Must satisfy token0 < token1 for canonical ordering.

  • poolHook Address of pool hook (zero address = no hook).

  • poolParams ABI-encoded initialization parameters specific to the pool type.


Token Ordering Rules

LBAMM will reorder tokens internally if necessary.

However:

You should always supply tokens in sorted order (token0 < token1).

Incorrect ordering may:

  • Cause confusion in offchain simulation
  • Cause confusion when adding liquidity with pool creation

Best practice:

const [token0, token1] =
tokenA.toLowerCase() < tokenB.toLowerCase()
? [tokenA, tokenB]
: [tokenB, tokenA];

LP Fee Configuration

Fixed Fee

Provide BPS directly:

details.fee = 300; // 3.00%

Constraints:

  • Must be ≤ 10000
  • If > 10000 and not 55555 with a non-zero pool hook → revert

Dynamic Fee

Use sentinel:

uint16 constant DYNAMIC_POOL_FEE_BPS = 55_555;
details.fee = 55_555;

Requirements:

  • details.poolHook MUST be non-zero.
  • If fee == 55_555 and poolHook == address(0) → revert.
  • Pool hook must implement dynamic fee selection.

Hook Validation Order

During creation:

  1. token0 hook
  2. token1 hook
  3. pool hook

Hooks:

  • Receive poolId
  • May revert
  • May enforce custom restrictions

Token hooks:

  • May block pool creation.
  • Do not charge fees through LBAMM core during creation.

Custom pool types may also enforce additional restrictions.


Calling createPool

function createPool(
PoolCreationDetails memory details,
bytes calldata token0HookData,
bytes calldata token1HookData,
bytes calldata poolHookData,
bytes calldata liquidityData
) external returns (bytes32 poolId, uint256 deposit0, uint256 deposit1);

Parameter Breakdown

details

The full PoolCreationDetails struct defining:

  • Pool type
  • LP fee (or dynamic sentinel)
  • token0 / token1
  • pool hook
  • Pool-specific initialization parameters

This struct deterministically defines the poolId.


token0HookData

Arbitrary calldata passed to token0’s hook during pool creation.

  • Only used if token0 has a hook configured and pool creation triggers validation.
  • May contain authorization data, configuration parameters, or validation proofs.
  • If token0 does not use hooks for creation validation, this may be empty ("0x").

token1HookData

Same semantics as token0HookData, but for token1.

  • Passed directly to token1’s hook during validation.
  • Ignored if token1 has no relevant hook logic for pool creation.

poolHookData

Arbitrary calldata passed to the pool hook during pool-level validation.

  • Used only if details.poolHook is non-zero.
  • Must match the expectations of the pool hook contract.
  • Ignored if no pool hook is configured.

Hooks may revert during validation, which will revert the entire pool creation.


liquidityData

Optional calldata that triggers an immediate addLiquidity call after pool creation.

Requirements:

  • Must be a valid ABI-encoded call to addLiquidity, including the function selector.
  • Must target the pool being created.
  • Must conform to the pool type’s expected liquidity parameters.
  • If malformed or if the addLiquidity call reverts → the entire transaction reverts.

If liquidityData is empty ("0x"):

  • Pool is created with zero liquidity.
  • deposit0 and deposit1 will both be zero.

If valid liquidity data is provided:

  • Liquidity is added atomically.
  • deposit0 and deposit1 will reflect the deposited token amounts.
  • Native token value (if supplied) must be consumed by the liquidity call or the transaction will revert.

Solidity Example

PoolCreationDetails memory details = PoolCreationDetails({
poolType: address(poolType),
fee: 300, // 3%
token0: token0,
token1: token1,
poolHook: address(0),
poolParams: abi.encode(/* pool-specific params */)
});

(bytes32 poolId,,) = amm.createPool(
details,
"",
"",
"",
"" // no initial liquidity
);

TypeScript Example (viem-style)

const details = {
poolType: poolTypeAddress,
fee: 300,
token0,
token1,
poolHook: zeroAddress,
poolParams: encodedParams
};

const { request } = await publicClient.simulateContract({
address: ammAddress,
abi: ammAbi,
functionName: "createPool",
args: [
details,
"0x", // token0HookData
"0x", // token1HookData
"0x", // poolHookData
"0x" // liquidityData
]
});

const hash = await walletClient.writeContract(request);

Checking If Pool Already Exists

Use pool type:

bytes32 poolId = poolType.computePoolId(details);

Then query core:

PoolState memory poolState = amm.getPoolState(poolId);
bool initialized = poolState.token0 != address(0);

If state is initialized → pool exists.

There is no pool enumeration in core. Index PoolCreated events offchain.


PoolCreated Event

event PoolCreated(
address indexed poolType,
address indexed token0,
address indexed token1,
bytes32 poolId,
address poolHook
);

Common Failure Cases

Pool creation will revert if:

  • token0 == token1
  • Fee > 10000 and not sentinel
  • Sentinel used without pool hook
  • Pool with same poolId already exists
  • Token hook validation fails
  • Pool hook validation fails
  • Custom pool type rejects parameters
  • liquidityData selector does not match addLiquidity
  • addLiquidity call reverts
  • Native token value supplied but unused

All failures revert atomically.


Design Considerations

  • poolHook is immutable after creation.
  • poolType is immutable.
  • LP fee configuration is encoded into poolId.
  • Offchain simulation is strongly recommended before submission.
  • Pool existence must be indexed offchain via events.

Limit Break

TwitterLimitBreak.comMedium

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

Privacy PolicyTerms of ServiceCookie PolicyDo Not Sell My Info