Initializing a Uniswap v3 Pool
Creating a new Uniswap v3 pool is the first step if you want to enable trading for a token pair that doesnāt have a pool yet. Letās walk through the process step by step.
Prerequisites
Before initializing a pool, you need:
- Two ERC-20 tokens (or one token and ETH/WETH)
- A fee tier (0.05%, 0.3%, or 1%)
- An initial price for the token pair
Understanding Pool Initialization
When you initialize a Uniswap v3 pool, youāre:
- Creating a new pool contract for the token pair
- Setting the initial price (as sqrtPriceX96)
- Preparing the pool to accept liquidity
However, the pool wonāt be usable until someone adds liquidity to it.
Code Example: Initializing a Pool
Hereās a simplified example of how to initialize a Uniswap v3 pool using Solidity:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
contract PoolInitializer {
address public factory;
constructor(address _factory) {
factory = _factory;
}
function createAndInitializePool(
address tokenA,
address tokenB,
uint24 fee,
uint160 sqrtPriceX96
) external returns (address pool) {
// Create the pool
pool = IUniswapV3Factory(factory).createPool(tokenA, tokenB, fee);
// Initialize the pool with the starting price
IUniswapV3Pool(pool).initialize(sqrtPriceX96);
return pool;
}
}
Calculating the Initial sqrtPriceX96
The trickiest part is calculating the correct sqrtPriceX96
value for your initial price. Hereās a helper function:
function calculateSqrtPriceX96(uint256 price, uint8 decimals0, uint8 decimals1) public pure returns (uint160) {
// Adjust for decimal differences between tokens
uint256 adjustedPrice = price * 10**(decimals0 - decimals1);
// Calculate square root
uint256 sqrtPrice = sqrt(adjustedPrice * 10**18);
// Scale by 2^96
return uint160((sqrtPrice * (2**96)) / 10**9);
}
// Helper function to calculate square root
function sqrt(uint256 x) internal pure returns (uint256) {
if (x == 0) return 0;
uint256 z = (x + 1) / 2;
uint256 y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
return y;
}
Example: Initializing an ETH/USDC Pool
Letās say we want to create an ETH/USDC pool with a 0.3% fee, and the initial price is 2000 USDC per ETH:
// Addresses for Ethereum mainnet
address WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
uint24 fee = 3000; // 0.3%
// Calculate sqrtPriceX96 for 2000 USDC per ETH
// WETH has 18 decimals, USDC has 6 decimals
uint256 price = 2000 * 10**6; // 2000 with USDC's 6 decimals
uint160 sqrtPriceX96 = calculateSqrtPriceX96(price, 6, 18);
// Create and initialize the pool
address pool = createAndInitializePool(USDC, WETH, fee, sqrtPriceX96);
In Uniswap v3, the token with the lower address value is considered token0, and the other is token1. The price is always expressed as the amount of token1 per token0.
What Happens After Initialization?
After initialization, the pool exists but has no liquidity. Itās like an empty marketplace with a price tag but no goods to trade.
The pool will remain in this state until someone adds liquidity. The initial price you set is important because:
- It determines the starting point for liquidity provision
- It affects how tokens need to be provided when adding the first liquidity
- If set incorrectly, it can make the pool less efficient or harder to use
In the next section, weāll learn how to add liquidity to our newly created pool.