Create a pool
Centrifuge enables the tokenization and management of pools on multiple chains. This guide walks you through the process of creating a pool, launching share classes, and deploying share tokens using the Centrifuge protocol.
Core concepts
Pool
A Pool represents a distinct investment product or strategy. Each pool can exist across multiple chains, identified globally by a unique poolId.
Share class
Each pool can have multiple share classes, each with its own share token. These tokens represent claims to the underlying assets or yield and can be permissioned or permissionless.
Share token
Each share class is deployed as a token (ERC-20 compatible) on every supported network. These tokens have a transfer hook to enable permission logic.
Pool parameters
When creating a pool, you need to choose two key parameters that cannot be changed after creation: the hub chain and the denomination currency.
Hub chain
The hub chain is the primary network where your pool is created and managed. All administrative transactions (such as deploying tokens and vaults, updating permissions, and managing requests) are submitted on the hub chain and then bridged to all other spoke chains where the pool operates.
Choosing the right hub chain is important because:
- It cannot be changed: Once a pool is created on a hub chain, migrating to a different hub chain would require creating an entirely new pool and migrating all users.
- Transaction costs: All management operations incur gas costs on the hub chain.
For more details on how the hub and spoke architecture works, read more about the architecture here.
Denomination currency
The denomination currency is the accounting unit for your pool. It does not restrict which assets investors can deposit or redeem, those are determined by which vaults you deploy. Instead, it serves as an intermediate unit that the protocol uses internally for pricing and accounting.
How pricing works
The protocol uses two prices to convert between deposit assets and shares:
pricePoolPerAsset: The value of one asset unit in pool currency units (e.g., 1 USDC = 1.0 USD)pricePoolPerShare: The value of one share unit in pool currency units (e.g., 1 share = 1.05 USD)
When an investor deposits assets, the conversion flows through the pool currency:
Asset amount → Pool currency amount → Share amount
For redemptions, the flow reverses: shares convert to pool currency via pricePoolPerShare, then to payout assets via pricePoolPerAsset.
Where the denomination currency matters
- Asset price oracles: If you accept deposit assets that are not pegged 1:1 to the pool currency (e.g., ETH deposits into a USD-denominated pool), you need to configure a price oracle on the hub chain to provide the
pricePoolPerAssetfor those assets - Onchain accounting: If you use onchain accounting, all calculations are expressed in this currency
Note that shares and pool currency always use the same number of decimals, so no decimal conversion is needed between them.
Choosing a denomination currency
You can use:
- Fiat currencies: Use
newAssetId(isoCode)whereisoCodeis the ISO 4217 numeric code (e.g.,840for USD,978for EUR) - Registered assets: Any ERC20 token that has been registered as an asset on any chain
Step-by-step: creating a pool
1. Derive the unique pool ID
To deploy a pool across different networks, choose which network you want to use as the Hub network. This will be where you create the pool, manage permissions, and control all other networks.
Use the centrifugeId as the network identifier of the Hub chain and derive a network-specific PoolId.
PoolId poolId = hubRegistry.poolId(centrifugeId, 1); // Derive a unique PoolId using centrifugeId and a local identifier
centrifugeId: View all possible centrifuge IDs here1: A local identifier.
2. Create the pool
Call the createPool function with the derived PoolId, the pool manager, and the denomination currency.
hub.createPool(poolId, msg.sender, newAssetId(840));
Currently, pool creation is still permissioned, while the protocol is in its initial rollout. Pool creation will be opened up permissionlessly in the following months.
3. Set metadata
Optionally set metadata to describe the pool:
hub.setPoolMetadata(poolId, bytes("Testing pool"));
This can include information to be shown in the UI.
4. Notify pool registration
Once created, the pool must notify the other networks of its existence. This should be called for every centrifugeId where the pool is going to be launched.
hub.notifyPool{value: gas}(poolId, centrifugeId, msg.sender);
gas: The amount of native currency to cover cross-chain messaging costs (excess will be refunded)msg.sender: Address to receive any excess gas refund
Adding share classes
Each pool can support multiple share classes, e.g., for different tranches or investor types.
1. Preview share class ID
Before creating a share class, preview its ID to use for further configuration.
ShareClassId scId = shareClassManager.previewNextShareClassId(poolId);
2. Add the share class
Add a new share class with a name, symbol, and optional metadata:
hub.addShareClass(poolId, "Tokenized MMF", "MMF", bytes32(bytes("1")));
"Tokenized MMF": ERC20 token name"MMF": ERC20 token symbolbytes32(bytes("1")): Salt, to be used for deterministic deployments and vanity addresses. Needs to be globally unique.
3. Deploy the share token(s)
Once created, the pool must notify the other networks of each share class. This should be called for every centrifugeId where the share token is going to be launched.
For each token, choose the hook that you want:
fullRestrictions: any user needs to be added to the memberlist for every deposit/redeem request.redemptionRestrictions: any user needs to be added to the memberlist only for redeem requests.freezeOnly: users don't need to be added for requests, but it is possible to freeze users.address(0): token is fully permissionless.
hub.notifyShareClass{value: gas}(poolId, scId, centrifugeId, bytes32(bytes20(hook)), msg.sender);
gas: The amount of native currency to cover cross-chain messaging costs (excess will be refunded)msg.sender: Address to receive any excess gas refund
This will deploy the ERC20 share token.