Integrating Balancer Liquidity For Swap Aggregators
This page serves as a central hub for aggregators seeking vital information and resources to seamlessly integrate with Balancer v3 liquidity. Should you require additional assistance or find any gaps in the provided information, our team is readily available to support you.
Making Swaps
The core concepts of executing Swaps are the same for any programming language or framework:
- The sender must approve the Vault (not the Router) for each swap input token
- Token amount inputs/outputs are always in the raw token scale, e.g. 1 USDC should be sent as 1000000 because it has 6 decimals
- Transactions are always sent to the Router
- There are two different swap kinds:
- ExactIn: Where the user provides an exact input token amount.
- ExactOut: Where the user provides an exact output token amount.
- There are two subsets of a swap:
- Single Swap: A swap, tokenIn > tokenOut, using a single pool. This is the most gas efficient option for a swap of this kind.
- Multi-path Swaps: Swaps involving multiple paths but all executed in the same transaction. Each path can have its own (or the same) tokenIn/tokenOut.
- Balancer v2 used the concept of poolIds, this is no longer used in v3 which always uses pool address
Balancer Routers
In the Balancer v3 architecture, Routers serve as the pivotal interface for users, facilitating efficient interaction with the underlying Vault primitives. Rather than directly engaging with the Vault, users are encouraged to utilize Routers as their primary entry point.
Note - the sender must approve the Vault (not the Router) for each swap input token
Swap Fees
Work in progress
Dynamic Swap Fees and Hooks are still WIP and not finalised
Swap fees come in two different forms for v3 pools:
- Static Swap Fee:
- Initially set as part of the pool registration.
- Authorized addresses can then change the value by invoking the vault.setStaticSwapFeePercentage(address pool, uint256 swapFeePercentage) function.
- If the staticSwapFeePercentage is changed, it will emit an event:
SwapFeePercentageChanged(pool, swapFeePercentage);
setStaticSwapFeePercentage
can also be called as part of a regular hook
- Dynamic Swap Fee:
- At registration pools can be set up to use dynamic swap fees.
- The Vault uses the
_getSwapFeePercentage(PoolConfig memory config)
to fetch the swap fee from the pool. This function can implement arbitrary logic. - Even when a pool is set to use dynamic swap fees, it still maintains a static swap fee. However, this static fee is not utilized.
The pseudo logic to determine how swap fee is calculated looks like:
swapFeePercentage =
Pool has DynamicSwapFee => call DynamicSwapFeeHook in the pool
else => load static Swap fee percentage from Vault
Simulating Swaps Using Query Functions
Queries provide the ability to simulate an operation and find its result without executing a transaction. Balancer Routers provide a query for all state changing liquidity operations including single and multi-path swap functions, e.g. querySwapSingleTokenExactIn
. The following sections link to examples showing how queries can be used.
Note - for onchain integrations queries should not be used to set limits due to possible manipulation via frontrunning.
Single Swaps
For token to token swap through a single pool swapSingleTokenExactIn and swapSingleTokenExactOut are the most gas efficient functions to use.
Checkout Javascript and Solidity examples here.
Multi-path Swaps
Swaps paths constructed of steps through multiple pools/tokens can be made using swapExactIn and swapExactOut functions.
A SwapPathStep
is defined as:
struct SwapPathStep {
address pool;
IERC20 tokenOut;
}
and paths can include add/remove liquidity steps by using the address of the respective pool. For example, the following SwapPathExactAmountIn
would execute a swap of USDC to BAL then add liquidity to the 80/20 BAL/WETH pool.
// Note - pseudo code
SwapPathExactAmountIn {
tokenIn: USDC
// for each step:
// if tokenIn == pool use removeLiquidity SINGLE_TOKEN_EXACT_IN
// if tokenOut == pool use addLiquidity UNBALANCED
steps: [
{
pool: '0xBAL_USDC_POOL',
tokenOut: '0xBAL'
},
{
pool: '0xB-80BAL-20WETH_POOL',
tokenOut: '0xB-80BAL-20WETH_POOL'
}
]
exactAmountIn: 1000000,
minAmountOut: 100000
}
Checkout Javascript and Solidity examples here.
Fetching Pool Data
Balancer API
Work in progress
The API is currently a WIP and some info in this section is still a WIP
The Balancer API can be used to retrieve a list of v3 pools and immutable data for calculating swaps. The API is running as a graphql server and is deployed at https://api-v3.balancer.fi.
The following query can be used to fetch v3 pools with relevant static data used for swap calculations:
query MyQuery {
poolGetPools(where: {vaultVersionIn: 3}) {
id
type
dynamicData {
swapEnabled
}
displayTokens {
address
weight
}
}
}
Onchain Data
The following view functions are available on the Vault contract and can be used to find onchain pool state.
Get pool tokens, balances and scaling factors:
/**
* @notice Gets the raw data for a pool: tokens, raw balances, scaling factors.
* @return tokenConfig Pool's token configuration
* @return balancesRaw Corresponding raw balances of the tokens
* @return scalingFactors Corresponding scalingFactors of the tokens
*/
function getPoolTokenInfo(
address pool
)
external
view
returns (TokenConfig[] memory tokenConfig, uint256[] memory balancesRaw, uint256[] memory scalingFactors);
where TokenConfig
:
/**
* @dev Encapsulate the data required for the Vault to support a token of the given type.
* For STANDARD tokens, the rate provider address must be 0, and paysYieldFees must be false.
* All WITH_RATE tokens need a rate provider, and may or may not be yield-bearing.
*
* @param token The token address
* @param tokenType The token type (see the enum for supported types)
* @param rateProvider The rate provider for a token (see further documentation above)
* @param paysYieldFees Flag indicating whether yield fees should be charged on this token
*/
struct TokenConfig {
IERC20 token;
TokenType tokenType;
IRateProvider rateProvider;
bool paysYieldFees;
}
Check if a pool is paused:
/**
* @notice Indicates whether a pool is paused.
* @param pool The pool to be checked
* @return True if the pool is paused
*/
function isPoolPaused(address pool) external view returns (bool);
Find a pools static swap fee:
/**
* @notice Fetches the static swap fee percentage for a given pool.
* @param pool The address of the pool whose static swap fee percentage is being queried
* @return The current static swap fee percentage for the specified pool
*/
function getStaticSwapFeePercentage(address pool) external view returns (uint256);
Find a pools dynamic swap fee:
/**
* @notice Query the current dynamic swap fee of a pool, given a set of swap parameters.
* @param pool The pool
* @param swapParams The swap parameters used to compute the fee
* @return success True if the pool has a dynamic swap fee and it can be successfully computed
* @return dynamicSwapFee The dynamic swap fee percentage
*/
function computeDynamicSwapFee(
address pool,
PoolSwapParams memory swapParams
) external view returns (bool, uint256);
Pool Maths Reference
Explore our GitHub repository containing reference mathematical implementations, in Javascript and Python, for supported Balancer pool types. Designed to assist developers and integrators in understanding the underlying swap calculations, these implementations can be imported as a packages into your project or serve as a reference for your own implementation.
Work in progress
The reference repo is still a WIP. For now please see the SOR repo for implementation examples.
Boosted Pools
Work in progress
This section will contain more details once code is finalised
Hooks
Work in progress
This section will contain more details once code is finalised