Deploy a Custom AMM Using a Factory
This section is for developers looking to deploy a custom pool contract that has already been written. If you are looking to design a custom AMM with a novel invariant, start here.
Balancer recommends that custom pools be deployed via a factory contract because our off-chain infrastructure uses the factory address as a means to identify the type of pool, which is important for integration into the UI, SDK, and external aggregators.
To fully set up a new custom pool so that normal liquidity operations and swaps are enabled, five required steps must be taken:
- Deploy a factory contract that inherits from BasePoolFactory.sol
- Deploy the pool contract using the factory's
_create
function - Register the pool using the factory's
_registerPoolWithVault
function - Use Permit2 to approve the Router to spend the tokens that will be used to initialize the pool
- Call
router.initialize()
to seed the pool with initial liquidity
Tips
To see example foundry scripts for deploying a custom pool using a factory, check out Scaffold Balancer v3
Creating a Custom Pool Factory Contract
A factory contract should inherit the BasePoolFactory.sol abstract contract, which sets the table for deploying pools with CREATE3
and streamlines the registration process.
Below, we present an example custom pool factory that uses the ConstantSumPool
contract from Build your custom AMM
Factory Constructor Parameters
IVault vault
: The address of the Balancer vaultuint32 pauseWindowDuration
: The period, starting from deployment of a factory, during which pools can be paused and unpaused, see FactoryWidePauseWindow.solbytes memory creationCode
: The creation bytecode of the pool contract used byCREATE3
for deployment
Pool Deployment Parameters
bytes memory constructorArgs
: The abi encoded constructor args for the custom poolbytes32 salt
: Used to compute a unique, deterministic address for each pool deployment
Pool Registration Parameters
TokenConfig[] memory tokens
: An array of descriptors for the tokens the pool will manage, see Token Typesuint256 swapFeePercentage
: Fee charged for each swap. For more information, see Swap feesbool protocolFeeExempt
: If true, the pool's initial aggregate fees will be set to 0PoolRoleAccounts memory roleAccounts
: Addresses allowed to change certain pool settings, see Pool Role Permissionsaddress poolHooksContract
: Contract that implements the hooks for the pool. If no hooks, use the zero addressLiquidityManagement memory liquidityManagement
: Specifies support for Custom Liquidity Operations
Info
Although deploying pools via a factory contract is the recommended approach, it is not mandatory since it is possible to call vault.registerPool
directly.
Initializing a Custom Pool
After a custom pool has been deployed and registered, the next step is to add initial liquidity to the pool, which is a three step process:
- Ensure the Permit2 contract has been granted sufficient allowance to spend tokens on behalf of the
msg.sender
- Transfer sufficient allowance to the Router with
Permit2.approve
- Call
router.initialize()
After a pool has been initialized, normal liquidity operations and swaps are instantly enabled.