Skip to content

Commit 4d2ff0e

Browse files
authored
Revise create-pool guide with detailed methods and tips
Updated the create-pool guide to enhance clarity and detail on pool creation methods, including atomic creation with liquidity and best practices for initializing pools without liquidity. Added security considerations and gas optimization benefits.
1 parent 05ef916 commit 4d2ff0e

1 file changed

Lines changed: 102 additions & 23 deletions

File tree

docs/sdk/v4/guides/advanced/create-pool.md

Lines changed: 102 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ sidebar_position: 2
88

99
In this example we will use **ethers.js** and the **Uniswap v4 SDK** to create pools on Uniswap v4. Uniswap v4 is a popular destination for creating markets due to its:
1010

11-
- Proven track record and battle-tested codebase
12-
- Concentrated liquidity, unlocks capital efficiency
13-
- Flexible pool design through dynamic fees and hooks
14-
- Gas-efficient architecture
11+
- Proven track record and battle-tested codebase (over $2.75 trillion in cumulative volume)
12+
- Concentrated liquidity, unlocking capital efficiency
13+
- Flexible pool design through dynamic fees and hooks (150+ hooks already developed)
14+
- Gas-efficient singleton architecture (99.99% cheaper pool creation)
15+
- Native ETH support without wrapping
16+
- Flash accounting system for optimized transactions
1517
- Integrations with alternative trading venues
1618

1719
For more information, developers should see [Uniswap v4 Overview](/contracts/v4/overview)
@@ -21,16 +23,20 @@ For this guide, the following Uniswap packages are used:
2123
- [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk)
2224
- [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core)
2325

24-
## Configuration
26+
## Two Approaches to Pool Creation
2527

26-
To initialize a Uniswap v4 Pool _without initial liquidity_, developers should call [`PoolManager.initialize()`](/contracts/v4/concepts/PoolManager)
28+
Uniswap v4 offers two methods for creating pools:
2729

28-
Creating a pool without liquidity may be useful for "reserving" a pool for future use, when initial liquidity is not available, or when external market makers would provide the starting liquidity.
30+
1. **Initialize pool without liquidity** - Using `PoolManager.initialize()`
31+
2. **Atomic pool creation with liquidity** - Using `PositionManager` with multicall (recommended)
32+
33+
## Recommended: Atomic Pool Creation with Initial Liquidity
34+
35+
**Uniswap v4's PositionManager supports atomic creation of a pool and initial liquidity using multicall.** Developers can create a trading pool with liquidity in a single transaction. This is the recommended approach as it avoids the security risks of empty pools.
2936

3037
### Configure the Pool
3138

3239
We will first create an example configuration `CurrentConfig` in `config.ts`. It has the format:
33-
3440
```typescript
3541
export const CurrentConfig: ExampleConfig = {
3642
env: Environment.MAINNET,
@@ -49,27 +55,76 @@ export const CurrentConfig: ExampleConfig = {
4955
}
5056
```
5157

52-
> For native token pairs (Ether), use `ADDRESS_ZERO` as `currency0`
58+
> For native token pairs (Ether), use `CurrencyLibrary.ADDRESS_ZERO` as `currency0`
5359
5460
[PoolKey](/contracts/v4/reference/core/types/PoolKey) uniquely identifies a pool
5561

5662
- _Currencies_ should be sorted, `uint160(currency0) < uint160(currency1)`
5763
- _lpFee_ is the fee expressed in pips, i.e. 3000 = 0.30%
5864
- _tickSpacing_ is the granularity of the pool. Lower values are more precise but may be more expensive to trade on
59-
- _hookContract_ is the address of the hook contract
65+
- _hookContract_ is the address of the hook contract (use zero address if no hooks)
6066

6167
A note on `tickSpacing`:
6268

6369
Lower tick spacing provides improved price precision; however, smaller tick spaces will cause swaps to cross ticks more often, incurring higher gas costs.
6470

65-
## Call `initialize` of Pool Manager contract
71+
### Atomic Creation with PositionManager
72+
```typescript
73+
import { ethers } from 'ethers'
74+
import { IPositionManager } from '@uniswap/v4-periphery/contracts/interfaces/IPositionManager.sol'
75+
76+
const POSITION_MANAGER_ADDRESS = '0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e' // Ethereum mainnet
77+
const provider = getProvider()
78+
const signer = new ethers.Wallet(PRIVATE_KEY, provider)
79+
const positionManager = new ethers.Contract(
80+
POSITION_MANAGER_ADDRESS,
81+
POSITION_MANAGER_ABI,
82+
signer
83+
)
84+
85+
// Prepare multicall parameters
86+
const actions = [Actions.POOL_INITIALIZE, Actions.MINT_POSITION, Actions.SETTLE_PAIR]
87+
const params = new Array(3)
88+
89+
// Initialize pool
90+
import {IPoolInitializer_v4} from "v4-periphery/src/interfaces/IPoolInitializer_v4.sol"
91+
params[0] = ethers.utils.defaultAbiCoder.encode(
92+
['tuple(address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks)', 'uint160'],
93+
[CurrentConfig.poolKey, startingPrice]
94+
)
95+
96+
// Mint position
97+
params[1] = ethers.utils.defaultAbiCoder.encode(
98+
['tuple(address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks)', 'int24', 'int24', 'uint256', 'uint128', 'uint128', 'address', 'bytes'],
99+
[CurrentConfig.poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, recipient, '0x']
100+
)
101+
102+
// Settle tokens
103+
params[2] = ethers.utils.defaultAbiCoder.encode(
104+
['address', 'address'],
105+
[CurrentConfig.poolKey.currency0, CurrentConfig.poolKey.currency1]
106+
)
66107

67-
Now to initialize the `Pool` we need to call the `initialize` function of the Pool Manager Contract.
68-
To construct the Pool Manager Contract we need to provide the address of the contract, its ABI and a provider connected to an RPC endpoint.
108+
// Execute atomically
109+
const result = await positionManager.modifyLiquidities(
110+
ethers.utils.defaultAbiCoder.encode(['uint256[]', 'bytes[]'], [actions, params]),
111+
Math.floor(Date.now() / 1000) + 60 // 60 second deadline
112+
)
113+
```
69114

115+
- the _startingPrice_ is expressed as sqrtPriceX96: `floor(sqrt(token1 / token0) * 2^96)`
116+
- i.e. `79228162514264337593543950336` is the starting price for a 1:1 pool
117+
118+
## Alternative: Initialize Pool Without Liquidity
119+
120+
To initialize a Uniswap v4 Pool _without initial liquidity_, developers should call [`PoolManager.initialize()`](/contracts/v4/concepts/PoolManager)
121+
122+
Creating a pool without liquidity may be useful for "reserving" a pool for future use, when initial liquidity is not available, or when external market makers would provide the starting liquidity.
123+
124+
### Call `initialize` of Pool Manager contract
70125
```typescript
71126
import { ethers } from 'ethers'
72-
const POOL_MANAGER_ADDRESS = '0x000000000004444c5dc75cB358380D2e3dE08A90' // Replace with actual StateView contract address
127+
const POOL_MANAGER_ADDRESS = '0x000000000004444c5dc75cB358380D2e3dE08A90' // Ethereum mainnet
73128
const POOL_MANAGER_ABI = [...]; // Import or define the ABI for PoolManager contract
74129

75130
const provider = getProvider() // Provide the right RPC address for the chain
@@ -84,29 +139,53 @@ const poolManager = new ethers.Contract(
84139
We get the `POOL_MANAGER_ADDRESS` for our chain from [Uniswap Deployments](/contracts/v4/deployments).
85140

86141
Pools are initialized with a starting price
87-
88142
```typescript
89143
const result = await poolManager.initialize(
90144
CurrentConfig.poolKey,
91145
startingPrice
92146
)
93147
```
94148

95-
- the _startingPrice_ is expressed as sqrtPriceX96: `floor(sqrt(token1 / token0) * 2^96)`
96-
- i.e. `79228162514264337593543950336` is the starting price for a 1:1 pool
97-
98149
Now the pool is initialized and you can add liquidity to it.
99150

100-
## Important Note on Initial Liquidity
151+
## Important Security Considerations
152+
153+
### Risks of Empty Pools
101154

102-
When creating a new pool, it's critical to understand that initializing a pool without liquidity can be dangerous. An empty pool's spot price is freely manipulatable since there is no liquidity to resist price movements.
155+
When creating a new pool, it's **critical** to understand that initializing a pool without liquidity can be dangerous. An empty pool's spot price is freely manipulatable since there is no liquidity to resist price movements.
103156

104157
This means that on the first liquidity provision, if proper slippage parameters are not set:
105158

106159
1. Malicious actors can manipulate the price before the first position is minted
107160
2. The first position can be mispriced and have incorrect asset ratios
161+
3. Front-running attacks can extract value from the initial LP
162+
163+
### Best Practices for Safe Pool Creation
164+
165+
**Strongly Recommended:**
166+
- Use the atomic pool creation method with PositionManager (shown above) to create pool + liquidity in one transaction
167+
- This eliminates the window for price manipulation attacks
168+
169+
**If you must initialize without liquidity:**
170+
- Add liquidity immediately after pool creation in the same transaction block
171+
- Always use strict slippage parameters when minting the first position
172+
- Set appropriate `amount0Max` and `amount1Max` limits
173+
- Consider using a private mempool or flashbots to prevent front-running
174+
- Monitor the pool state before adding liquidity
175+
176+
Reference our [Mint Position guide](/sdk/v4/guides/liquidity/position-minting) for proper liquidity addition practices.
177+
178+
## Gas Optimization Benefits
179+
180+
Uniswap v4's singleton architecture provides significant gas savings:
181+
- **Pool creation**: Up to 99.99% cheaper than v3 (state update vs contract deployment)
182+
- **Multi-hop swaps**: No intermediate token transfers needed
183+
- **Flash accounting**: Only net balances settled via EIP-1153 transient storage
184+
- **Native ETH**: ~15% gas savings on ETH pairs (no wrapping/unwrapping)
108185

109-
To safely add the first liquidity to a new pool:
186+
## Additional Resources
110187

111-
- Always use appropriate slippage parameters when minting the first position
112-
- Consider adding liquidity immediately after pool creation in the same transaction. Reference our [Mint Position guide](/sdk/v4/guides/liquidity/position-minting) for proper liquidity addition practices.
188+
- [Position Manager Documentation](/contracts/v4/guides/position-manager)
189+
- [Uniswap v4 Hooks](https://uniswaphooks.com/) - Browse 150+ community hooks
190+
- [v4 Whitepaper](https://uniswap.org/whitepaper-v4.pdf)
191+
- [Hook Incubator Program](https://atrium.academy/uniswap) - Learn to build custom hooks

0 commit comments

Comments
 (0)