Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V4 create pool #788

Merged
merged 3 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 171 additions & 2 deletions docs/contracts/v4/quickstart/01-create-pool.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,175 @@
title: Create Pool
---

## 🚧 Under construction 🚧
## Context

Please see https://www.v4-by-example.org/initialize
Creating a pool on Uniswap v4 is permissionless and enables the trading of an asset. Uniswap v4 is a popular destination for creating markets due to its:

* Proven track record and battle-tested codebase
* Concentrated liquidity, unlocking capital efficiency
* Flexibile pool design through dynamic fees and hooks
* Gas-efficient architecture
* Integrations with alternative trading venues

For more information, developers should see [Uniswap v4 Overview](/contracts/v4/overview)

The guide covers two approaches to creating a pool:
1. Create a pool only
2. Create a pool and add initial liquidity, with one transaction

### Setup

Developing with Uniswap v4 _requires [foundry](https://book.getfoundry.sh)_

Install the dependencies:
```bash
forge install uniswap/v4-core
forge install uniswap/v4-periphery
```

## Guide: Create a Pool Only
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to clarify in what case would someone create a pool with no liquidity?


To initialize a Uniswap v4 Pool _without initial liquidity_, developers should call [`PoolManager.initialize()`](/contracts/v4/reference/core/interfaces/IPoolManager#initialize)

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

### 1. Configure the Pool

```solidity
import {PoolKey} from "v4-core/src/types/PoolKey.sol";

PoolKey memory pool = PoolKey({
currency0: currency0,
currency1: currency1,
fee: lpFee,
tickSpacing: tickSpacing,
hooks: hookContract
});
```
> For native token pairs (Ether), use `CurrencyLibrary.ADDRESS_ZERO` as `currency0`

[PoolKey](/contracts/v4/reference/core/types/PoolKey) uniquely identifies a pool

* _Currencies_ should be sorted, `uint160(currency0) < uint160(currency1)`
* _lpFee_ is the fee expressed in pips, i.e. 3000 = 0.30%
* _tickSpacing_ is the granularity of the pool. Lower values are more precise but may be more expensive to trade on
* _hookContract_ is the address of the hook contract

A note on `tickSpacing`:

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

As a reference, Uniswap v3 pools are configured with:

| Fee | Fee Value | Tick Spacing |
|-------|-----------|--------------|
| 0.01% | 100 | 1 |
| 0.05% | 500 | 10 |
| 0.30% | 3000 | 60 |
| 1.00% | 10_000 | 200 |

### 2. Call `initialize`
Pools are initialized with a starting price
```solidity
IPoolManager(manager).initialize(pool, startingPrice);
```
* the _startingPrice_ is expressed as sqrtPriceX96: `floor(sqrt(token1 / token0) * 2^96)`
- i.e. `79228162514264337593543950336` is the starting price for a 1:1 pool

## Guide: Create a Pool & Add Liquidity
Uniswap v4's `PositionManager` supports atomic creation of a pool and initial liquidity using [_multicall_](/contracts/v4/reference/periphery/base/Multicall_v4). Developers can create a trading pool, with liquidity, in a single transaction:

### 1. Initialize the parameters provided to `multicall()`
```solidity
bytes[] memory params = new bytes[](2);
```
* The first call, `params[0]`, will encode `initializePool` parameters
* The second call, `params[1]`, will encode a _mint_ operation for `modifyLiquidities`

### 2. Configure the pool
```solidity
PoolKey memory pool = PoolKey({
currency0: currency0,
currency1: currency1,
fee: lpFee,
tickSpacing: tickSpacing,
hooks: hookContract
});
```
> For native token pairs (Ether), use `CurrencyLibrary.ADDRESS_ZERO` as `currency0`

[PoolKey](/contracts/v4/reference/core/types/PoolKey) uniquely identifies a pool
* _Currencies_ should be sorted, `uint160(currency0) < uint160(currency1)`
* _lpFee_ is the fee expressed in pips, i.e. 3000 = 0.30%
* _tickSpacing_ is the granularity of the pool. Lower values are more precise but more expensive to trade
* _hookContract_ is the address of the hook contract

### 3. Encode the [`initializePool`](/contracts/v4/reference/periphery/base/PoolInitializer) parameters
Pools are initialized with a starting price
```solidity
params[0] = abi.encodeWithSelector(
PositionManager.initializePool.selector,
pool,
startingPrice
);
```
* the _startingPrice_ is expressed as _sqrtPriceX96_: `floor(sqrt(token1 / token0) * 2^96)`
- `79228162514264337593543950336` is the starting price for a 1:1 pool

### 4. Initialize the _mint-liquidity_ parameters
PositionManager's `modifyLiquidities` uses an encoded command system
```solidity
bytes memory actions = abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR));
```
* The first command `MINT_POSITION` creates a new liquidity position
* The second command `SETTLE_PAIR` indicates that tokens are to be paid by the caller, to create the position

### 5. Encode the `MINT_POSITION` parameters
```solidity
bytes[] memory mintParams = new bytes[](2);
mintParams[0] = abi.encode(pool, tickLower, tickUpper, liquidity, amount0Max, amount1Max, recipient, hookData);
```
* _pool_ the same `PoolKey` defined above, in pool-creation
* _tickLower_ and _tickUpper_ are the range of the position, must be a multiple of `pool.tickSpacing`
* _liquidity_ is the amount of liquidity units to add, see `LiquidityAmounts` for converting token amounts to liquidity units
* _amount0Max_ and _amount1Max_ are the maximum amounts of token0 and token1 the caller is willing to transfer
* _recipient_ is the address that will receive the liquidity position (ERC-721)
* _hookData_ is the optional hook data

### 6. Encode the `SETTLE_PAIR` parameters
Creating a position on a pool requires the caller to transfer `currency0` and `currency1` tokens
```solidity
mintParams[1] = abi.encode(pool.currency0, pool.currency1);
```

### 7. Encode the [`modifyLiquidites`](/contracts/v4/reference/periphery/PositionManager#modifyliquidities) call
```solidity
uint256 deadline = block.timestamp + 60;
params[1] = abi.encodeWithSelector(
posm.modifyLiquidities.selector, abi.encode(actions, mintParams), deadline
);
```

### 8. Approve the tokens
`PositionManager` uses `Permit2` for token transfers
* Repeat for both tokens
```solidity
// approve permit2 as a spender
IERC20(token).approve(address(permit2), type(uint256).max);

// approve `PositionManager` as a spender
IAllowanceTransfer(address(permit2)).approve(token, address(positionManager), type(uint160).max, type(uint48).max);
```

### 9. Execute the multicall
The `multicall` is used to execute multiple calls in a single transaction
```solidity
PositionManager(posm).multicall(params);
```
For pools paired with native tokens (Ether), provide `value` in the contract call
```solidity
PositionManager(posm).multicall{value: ethToSend}(params);
```
> Excess Ether is **NOT** refunded unless developers encoded `SWEEP` in the `actions` parameter

For a full end-to-end script, developers should see [v4-template's scripts](https://github.com/uniswapfoundation/v4-template/tree/main/script)
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,28 @@ forge install

---

## Manual

(TODO: Add instructions for manual setup)
## Manual Setup

After cloning the repository, and installing foundry, developers can manually set up their Uniswap v4 environment:

1. Initialize a foundry project
```bash
forge init . --force
```

2. Install dependencies
```bash
forge install uniswap/v4-core
forge install uniswap/v4-periphery
```

3. Set the `remappings.txt` to:
```
@uniswap/v4-core/=lib/v4-core/
forge-gas-snapshot/=lib/v4-core/lib/forge-gas-snapshot/src/
forge-std/=lib/v4-core/lib/forge-std/src/
permit2/=lib/v4-periphery/lib/permit2/
solmate/=lib/v4-core/lib/solmate/
v4-core/=lib/v4-core/
v4-periphery/=lib/v4-periphery/
```
Loading