Composite Liquidity Router API

The Composite Liquidity Router can be used to interact with Balancer onchain via state changing operations or used to query operations in an off-chain context.

Specialized router for two complex scenarios:

  1. ERC4626 Pools: Pools containing yield-bearing tokens (e.g., waDAI, waUSDC)
  2. Nested Pools: Pools where one or more tokens are BPTs from other pools

State-changing functions

ERC4626 Pool Operations

ERC4626 pools contain wrapped yield-bearing tokens. This router allows you to interact with them using only underlying tokens, automatically handling wrapping/unwrapping through Vault buffers.

addLiquidityUnbalancedToERC4626Pool

function addLiquidityUnbalancedToERC4626Pool(
    address pool,
    bool[] memory wrapUnderlying,
    uint256[] memory exactAmountsIn,
    uint256 minBptAmountOut,
    bool wethIsEth,
    bytes memory userData
) external payable returns (uint256 bptAmountOut);

Adds liquidity to an ERC4626 pool using underlying or wrapped tokens.

Example: Add liquidity to a [waDAI, waUSDC] pool using only DAI and USDC. The router automatically:

  1. Swaps DAI for waDAI through the Vault buffer
  2. Swaps USDC for waUSDC through the Vault buffer
  3. Adds liquidity to the pool

Note: Ensure that any buffers associated with the wrapped tokens in the ERC4626 pool have been initialized before initializing or adding liquidity to the "parent" pool, and also make sure limits are set properly.

Parameters:

NameTypeDescription
pooladdressAddress of the liquidity pool
wrapUnderlyingbool[] memoryFor each token, true = use underlying (e.g., DAI), false = use wrapped (e.g., waDAI)
exactAmountsInuint256[] memoryExact amounts of underlying/wrapped tokens in, sorted in token registration order
minBptAmountOutuint256Minimum amount of pool tokens to be received
wethIsEthboolIf true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
userDatabytes memoryAdditional (optional) data required for adding liquidity

Returns:

NameTypeDescription
bptAmountOutuint256Actual amount of pool tokens received

addLiquidityProportionalToERC4626Pool

function addLiquidityProportionalToERC4626Pool(
    address pool,
    bool[] memory wrapUnderlying,
    uint256[] memory maxAmountsIn,
    uint256 exactBptAmountOut,
    bool wethIsEth,
    bytes memory userData
) external payable returns (uint256[] memory amountsIn);

Adds liquidity proportionally to an ERC4626 pool using underlying or wrapped tokens.

Note: Ensure that any buffers associated with the wrapped tokens in the ERC4626 pool have been initialized before initializing or adding liquidity to the "parent" pool, and also make sure limits are set properly.

Parameters:

NameTypeDescription
pooladdressAddress of the liquidity pool
wrapUnderlyingbool[] memoryFor each token, true = use underlying (e.g., DAI), false = use wrapped (e.g., waDAI)
maxAmountsInuint256[] memoryMaximum amounts of underlying/wrapped tokens in, sorted in token registration order
exactBptAmountOutuint256Exact amount of pool tokens to be received
wethIsEthboolIf true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
userDatabytes memoryAdditional (optional) data required for adding liquidity

Returns:

NameTypeDescription
amountsInuint256[] memoryActual amounts of tokens added to the pool

removeLiquidityProportionalFromERC4626Pool

function removeLiquidityProportionalFromERC4626Pool(
    address pool,
    bool[] memory unwrapWrapped,
    uint256 exactBptAmountIn,
    uint256[] memory minAmountsOut,
    bool wethIsEth,
    bytes memory userData
) external payable returns (uint256[] memory amountsOut);

Removes liquidity proportionally from an ERC4626 pool, receiving underlying or wrapped tokens.

Parameters:

NameTypeDescription
pooladdressAddress of the liquidity pool
unwrapWrappedbool[] memoryFor each token, true = receive underlying, false = receive wrapped
exactBptAmountInuint256Exact amount of pool tokens provided
minAmountsOutuint256[] memoryMinimum amounts of each token, sorted in token registration order
wethIsEthboolIf true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
userDatabytes memoryAdditional (optional) data required for removing liquidity

Returns:

NameTypeDescription
amountsOutuint256[] memoryActual amounts of tokens received

Nested Pool Operations

Nested pools contain BPTs from other pools as tokens. This router handles the complexity of traversing multiple pool levels.

Important: Pools with "overlapping" tokens (where both parent and child pools contain the same token) are not supported.

addLiquidityUnbalancedNestedPool

function addLiquidityUnbalancedNestedPool(
    address parentPool,
    address[] memory tokensIn,
    uint256[] memory exactAmountsIn,
    address[] memory tokensToWrap,
    uint256 minBptAmountOut,
    bool wethIsEth,
    bytes memory userData
) external payable returns (uint256 bptAmountOut);

Adds liquidity to a nested pool using only the leaf tokens (tokens from child pools).

Example:

  • Parent pool: [BPT_A, BPT_B, USDC]
  • Child pool A: [DAI, USDT]
  • Child pool B: [WETH, WBTC]
  • tokensIn could be: [DAI, USDT, WETH, WBTC, USDC]

The router:

  1. Adds DAI and USDT to pool A to get BPT_A
  2. Adds WETH and WBTC to pool B to get BPT_B
  3. Adds BPT_A, BPT_B, and USDC to parent pool

Note: A nested pool is one in which one or more tokens are BPTs from another pool (child pool). Since there are multiple pools involved, the token order is not well-defined, and must be specified by the caller. If the parent or nested pools contain ERC4626 tokens that appear in the tokensToWrap list, they will be wrapped and their underlying tokens pulled as input, and expected to appear in tokensIn. Otherwise, they will be treated as regular tokens.

Parameters:

NameTypeDescription
parentPooladdressThe address of the parent pool (which contains BPTs of other pools)
tokensInaddress[] memoryAn array with all tokens from the child pools, and all non-BPT parent tokens, in arbitrary order
exactAmountsInuint256[] memoryAn array with the amountIn of each token, sorted in the same order as tokensIn
tokensToWrapaddress[] memoryA list of ERC4626 tokens which should be wrapped if encountered during pool traversal
minBptAmountOutuint256Expected minimum amount of parent pool tokens to receive
wethIsEthboolIf true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
userDatabytes memoryAdditional (optional) data required for the operation

Returns:

NameTypeDescription
bptAmountOutuint256The actual amount of parent pool tokens received

removeLiquidityProportionalNestedPool

function removeLiquidityProportionalNestedPool(
    address parentPool,
    uint256 exactBptAmountIn,
    address[] memory tokensOut,
    uint256[] memory minAmountsOut,
    address[] memory tokensToUnwrap,
    bool wethIsEth,
    bytes memory userData
) external payable returns (uint256[] memory amountsOut);

Removes liquidity from a nested pool, receiving the leaf tokens.

The router automatically:

  1. Exits the parent pool to get child BPTs
  2. Exits each child pool to get leaf tokens
  3. Unwraps ERC4626 tokens if requested

Note: A nested pool is one in which one or more tokens are BPTs from another pool (child pool). Since there are multiple pools involved, the token order is not well-defined, and must be specified by the caller. If the parent or nested pools contain ERC4626 tokens that appear in the tokensToUnwrap list, they will be unwrapped and their underlying tokens sent to the output. Otherwise, they will be treated as regular tokens.

Parameters:

NameTypeDescription
parentPooladdressThe address of the parent pool (which contains BPTs of other pools)
exactBptAmountInuint256The exact amount of parentPool tokens provided
tokensOutaddress[] memoryAn array with all tokens from the child pools, and all non-BPT parent tokens, in arbitrary order
minAmountsOutuint256[] memoryAn array with the minimum amountOut of each token, sorted in the same order as tokensOut
tokensToUnwrapaddress[] memoryA list of ERC4626 tokens which should be unwrapped if encountered during pool traversal
wethIsEthboolIf true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
userDatabytes memoryAdditional (optional) data required for the operation

Returns:

NameTypeDescription
amountsOutuint256[] memoryAn array with the actual amountOut of each token, sorted in the same order as tokensOut

Queries

queryAddLiquidityUnbalancedToERC4626Pool

function queryAddLiquidityUnbalancedToERC4626Pool(
    address pool,
    bool[] memory wrapUnderlying,
    uint256[] memory exactAmountsIn,
    address sender,
    bytes memory userData
) external returns (uint256 bptAmountOut);

Queries an addLiquidityUnbalancedToERC4626Pool operation without actually executing it.

Parameters:

NameTypeDescription
pooladdressAddress of the liquidity pool
wrapUnderlyingbool[] memoryFlags indicating whether the corresponding token should be wrapped or used as an ERC20
exactAmountsInuint256[] memoryExact amounts of underlying/wrapped tokens in, sorted in token registration order
senderaddressThe sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
userDatabytes memoryAdditional (optional) data required for the query

Returns:

NameTypeDescription
bptAmountOutuint256Expected amount of pool tokens to receive

queryAddLiquidityProportionalToERC4626Pool

function queryAddLiquidityProportionalToERC4626Pool(
    address pool,
    bool[] memory wrapUnderlying,
    uint256 exactBptAmountOut,
    address sender,
    bytes memory userData
) external returns (uint256[] memory amountsIn);

Queries an addLiquidityProportionalToERC4626Pool operation without actually executing it.

Parameters:

NameTypeDescription
pooladdressAddress of the liquidity pool
wrapUnderlyingbool[] memoryFlags indicating whether the corresponding token should be wrapped or used as an ERC20
exactBptAmountOutuint256Exact amount of pool tokens to be received
senderaddressThe sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
userDatabytes memoryAdditional (optional) data required for the query

Returns:

NameTypeDescription
amountsInuint256[] memoryExpected amounts of tokens added to the pool

queryRemoveLiquidityProportionalFromERC4626Pool

function queryRemoveLiquidityProportionalFromERC4626Pool(
    address pool,
    bool[] memory unwrapWrapped,
    uint256 exactBptAmountIn,
    address sender,
    bytes memory userData
) external returns (uint256[] memory amountsOut);

Queries a removeLiquidityProportionalFromERC4626Pool operation without actually executing it.

Parameters:

NameTypeDescription
pooladdressAddress of the liquidity pool
unwrapWrappedbool[] memoryFlags indicating whether the corresponding token should be unwrapped or used as an ERC20
exactBptAmountInuint256Exact amount of pool tokens provided for the query
senderaddressThe sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
userDatabytes memoryAdditional (optional) data required for the query

Returns:

NameTypeDescription
amountsOutuint256[] memoryExpected amounts of tokens to receive

queryAddLiquidityUnbalancedNestedPool

function queryAddLiquidityUnbalancedNestedPool(
    address parentPool,
    address[] memory tokensIn,
    uint256[] memory exactAmountsIn,
    address[] memory tokensToWrap,
    address sender,
    bytes memory userData
) external returns (uint256 bptAmountOut);

Queries an addLiquidityUnbalancedNestedPool operation without actually executing it.

Parameters:

NameTypeDescription
parentPooladdressThe address of the parent pool (which contains BPTs of other pools)
tokensInaddress[] memoryAn array with all tokens from the child pools, and all non-BPT parent tokens, in arbitrary order
exactAmountsInuint256[] memoryAn array with the amountIn of each token, sorted in the same order as tokensIn
tokensToWrapaddress[] memoryA list of ERC4626 tokens which should be wrapped if encountered during pool traversal
senderaddressThe sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
userDatabytes memoryAdditional (optional) data required for the operation

Returns:

NameTypeDescription
bptAmountOutuint256The expected amount of parent pool tokens to receive

queryRemoveLiquidityProportionalNestedPool

function queryRemoveLiquidityProportionalNestedPool(
    address parentPool,
    uint256 exactBptAmountIn,
    address[] memory tokensOut,
    address[] memory tokensToUnwrap,
    address sender,
    bytes memory userData
) external returns (uint256[] memory amountsOut);

Queries a removeLiquidityProportionalNestedPool operation without actually executing it.

Parameters:

NameTypeDescription
parentPooladdressThe address of the parent pool (which contains BPTs of other pools)
exactBptAmountInuint256The exact amount of parentPool tokens provided
tokensOutaddress[] memoryAn array with all tokens from the child pools, and all non-BPT parent tokens, in arbitrary order
tokensToUnwrapaddress[] memoryA list of ERC4626 tokens which should be unwrapped if encountered during pool traversal
senderaddressThe sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
userDatabytes memoryAdditional (optional) data required for the operation

Returns:

NameTypeDescription
amountsOutuint256[] memoryAn array with the expected amountOut of each token, sorted in the same order as tokensOut