Skip to content

Pool Factory Overview

Curve Pool Factories allow the permissionless deployment of liquidity pools, gauges, and LP tokens.

Every Factory contract from Curve comes with built-in functions designed to feed the MetaRegistry with information about the created pools. These functions will not be documented in this section. For more information, please read here.

Contract Source & Deployment

Factories are deployed on the Ethereum Mainnet as well as on Sidechains/L2. Although it might be the case that some pool types (e.g., cryptoswap pools) are not supported there yet.
A list of all deployed contracts can be found here.

Warning

The methods below might slightly vary depending on the Factory contract being examined. If there are any abnormalities or important standouts, they will be detailed as accurately as possible within the appropriate section!

Supported Pools

The various Curve Factories allow the deployment of pools with virtually any kind of asset combination, whether they are stable or volatile, rebasing or not, etc... Some pool variations (e.g., cryptoswap pool) might not be supported on sidechains/L2s yet.

Factory Description Supported Pools
StableSwap Regular StableSwap Plain and metapools
StableSwap-NG Improved StableSwap version Plain pools with up to eight coins and metapools (more here)
CryptoSwap Regular CryptoSwap Two-coin volatile assets (e.g., CRV<>ETH)
CryptoSwap-NG CryptoSwap New-Generation Two-coin volatile assets (e.g., CRV<>ETH)
Tricrypto-NG Improved Tricrypto version (here) Three-coin volatile assets (e.g., ETH<>BTC<>crvUSD)

For an easy, non-technical explanation of the pool variations, visit: https://resources.curve.fi/lp/pools/

How are contracts deployed?

Pool, gauge, or LP token contracts are created according to their implementation contracts set within the Factory. Contracts deployed by newer factories combine both liquidity pool and LP token, whereas for older ones, they are separate contracts.

There are two ways the contracts are deployed:

create_forwarder_to

Earlier factories like the regular stableswap or cryptoswap one use Vyper's built-in create_forwarder_to() function (renamed to create_minimal_proxy_to starting from Vyper version 0.3.4) to deploy liquidity pools, LP tokens, or gauge contracts.

The contracts then need to be initialized, which is done automatically.

Blueprint Contracts

Newer factories make use of blueprints (EIP-5202). The contracts are directly created from their corresponding blueprint implementations. This is the most desired and used method for all newly deployed factories.

Fee Receiver

The fee receiver is set within the Factory and is a uniform address for all the deployed pools through a factory. The address can be changed by the admin of the contract by calling the set_fee_receiver method and setting a new address.

fee_receiver

Factory.fee_receiver() -> address: view

Getter for the fee receiver of the pools admin fees.

Returns: fee receiver (address).

Source code
# fee receiver for all pools
fee_receiver: public(address)
>>> Factory.fee_receiver()
'0xeCb456EA5365865EbAb8a2661B0c503410e9B347'

set_fee_receiver

Factory.set_fee_receiver(_pool: address, _fee_receiver: address)

Guarded Method

This function is only callable by the admin of the contract.

Function to set a new fee receiver.

Input Type Description
_pool address This variable has no real use; insert a random address, otherwise the transaction will fail.
_fee_receiver address Address of the new fee receiver
Source code
# fee receiver for all pools
fee_receiver: public(address)

@external
def set_fee_receiver(_pool: address, _fee_receiver: address):
    """
    @notice Set fee receiver for all pools
    @param _pool Address of  pool to set fee receiver for.
    @param _fee_receiver Address that fees are sent to
    """
    assert msg.sender == self.admin  # dev: admin only
    self.fee_receiver = _fee_receiver
>>> Factory.set_fee_receiver('0x0000000000000000000000000000000000000000')    

Factory Contract Ownership

The admin is the owner of the Factory contract and has the ability to call admin-only functions. Ownership can be transferred by first committing to the transfer of ownership (commit_transfer_ownership), which then needs to be accepted by the future_admin (accept_transfer_ownership).

Most contracts are 'owned' by a proxy, which in turn is owned by the DAO. For some factories, the DAO is directly the owner.

admin

Factory.admin() -> address: view

Getter for the admin of the Factory.

Returns: admin (address).

Source code
admin: public(address)
>>> Factory.admin()

future_admin

Factory.future_admin() -> address: view

Getter for the future admin.

Returns: future admin (address).

Source code
future_admin: public(address)
>>> Factory.future_admin()

commit_transfer_ownership

Factory.commit_transfer_ownership(_addr: address):

Guarded Method

This function is only callable by the admin of the contract.

Function to commit a transfer of ownership. This function sets _addr as the future admin of the contract. These changes need to be applied via accept_transfer_ownership by the future admin itself.

Input Type Description
_addr address Address of the future admin
Source code
admin: public(address)
future_admin: public(address)

@external
def commit_transfer_ownership(_addr: address):
    """
    @notice Transfer ownership of this contract to `addr`
    @param _addr Address of the new owner
    """
    assert msg.sender == self.admin  # dev: admin only
    self.future_admin = _addr
>>> Factory.commit_transfer_ownership("whatever")

accept_transfer_ownership

Factory.accept_transfer_ownership():

Guarded Method

This function is only callable by the future_admin of the contract.

Function to accept the ownership transfer.

Source code
admin: public(address)
future_admin: public(address)

@external
def accept_transfer_ownership():
    """
    @notice Accept a pending ownership transfer
    @dev Only callable by the new owner
    """
    _admin: address = self.future_admin
    assert msg.sender == _admin  # dev: future admin only

    self.admin = _admin
    self.future_admin = empty(address)
>>> Factory.accept_transfer_ownership()

Implementations

Pool, gauge, or LP token contracts are created according to their implementation contracts set within the Factory.

Technical documentation was done separately for each factory, as they partially vary from each other. Please refer to the corresponding section.

Warning

Implementation contracts are upgradable. They can either be replaced, or additional implementation contracts can be added. Therefore, please always make sure to check the most recent ones.