Skip to content

Commit

Permalink
Uniswap V4 Docs (#645)
Browse files Browse the repository at this point in the history
  • Loading branch information
RobinNagpal authored Jan 11, 2024
1 parent 8aa3062 commit 1a77926
Show file tree
Hide file tree
Showing 32 changed files with 1,281 additions and 10 deletions.
3 changes: 1 addition & 2 deletions docs/contracts/permit2/_category_.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"label": "Permit2",
"position": 3,
"position": 4,
"collapsed": true
}

3 changes: 1 addition & 2 deletions docs/contracts/uniswapx/_category_.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"label": "UniswapX",
"position": 2,
"position": 3,
"collapsed": true
}

3 changes: 1 addition & 2 deletions docs/contracts/universal-router/_category_.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"label": "Universal Router",
"position": 2,
"position": 3,
"collapsed": true
}

2 changes: 1 addition & 1 deletion docs/contracts/v1/_category_.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"label": "V1 Protocol",
"position": 5,
"position": 6,
"collapsed": true
}
2 changes: 1 addition & 1 deletion docs/contracts/v2/_category_.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"label": "V2 Protocol",
"position": 4,
"position": 5,
"collapsed": true
}
4 changes: 2 additions & 2 deletions docs/contracts/v3/_category_.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"label": "V3 Protocol",
"position": 1,
"position": 2,
"collapsed": false
}
}
5 changes: 5 additions & 0 deletions docs/contracts/v4/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"label": "V4 Protocol",
"position": 1,
"collapsed": false
}
43 changes: 43 additions & 0 deletions docs/contracts/v4/concepts/01-intro-to-v4.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
id: intro-to-v4
title: Introduction to Uniswap V4
sidebar_position: 0.5
---

Uniswap V4, the latest iteration of the Uniswap protocol, is a significant advancement in decentralized
exchanges (DEXs) and automated market makers (AMMs). Here are the key features of Uniswap V4:

**Customizability with Hooks:** Uniswap V4 introduces a new feature called "hooks", which are essentially smart
contracts that can be attached to liquidity pools. These hooks enable a high degree of customization, allowing
developers to implement specific functionalities at different points in a pool's lifecycle, such as before or after
swaps and liquidity modifications.

For example, hooks can enable order types (i.e. limit order), specially-tailored oracles, or custom AMM curves. The
flexibility of hooks allows for a broad range of innovations while maintaining the core efficiency of the platform.

**Singleton Contract for Efficiency:** A significant architectural change in Uniswap V4 is the introduction of a
Singleton contract. In previous versions, each token pair required a separate smart contract, leading to higher gas
costs, especially in multi-hop trades. The Singleton contract model consolidates all pools into a single contract,
significantly reducing gas costs for both trading and pool creation. This model allows for more efficient multi-hop
trades as tokens do not need to be transferred between multiple contracts. Additionally, creating a new pool in
V4 is 99% cheaper in gas costs compared to V3, lowering barriers for setting up new pools.

**Flash Accounting System:** Another innovative feature in Uniswap V4 is the "flash accounting" system. This system
allows users to efficiently chain together multiple actions in a single transaction, such as swap-and-add-liquidity.
The system tracks the net balances of inbound and outbound tokens; at the end of the transaction,
the contract verifies all debts have been settled. If the user hasn’t settled their debts, the entire transaction reverts,
ensuring security and efficiency. This system is similar to flash loans in concept and is part of the effort to
reduce gas costs and enhance transaction efficiency on the platform.

**Unlimited Fee Tiers:** Uniswap V4 allows unlimited fee tiers for various liquidity pools. This flexibility allows
for a more tailored approach in catering to a diverse range of assets and trading strategies. Each pool can have its
own unique fee tiers, optimizing the platform's appeal to a wider spectrum of users and market needs.

**Native ETH Support**: Uniswap V4 enhances user experience by enabling direct trading pairs with native ETH,
eliminating the need for WETH (Wrapped ETH). This simplification streamlines the trading process and lowers transaction
costs.

**Community-Driven Development and Innovation:** Uniswap V4 emphasizes a community-driven approach to development
and innovation. Since its code release, there has been active community engagement, with many issues, pull requests,
and unique feature ideas contributed by users. The protocol is
designed to encourage innovation, allowing the global community to shape the future of AMMs.
29 changes: 29 additions & 0 deletions docs/contracts/v4/concepts/02-1-overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
id: v4-architecture-overview
title: V4 Architecture Overview
sidebar_position: 1
---

# Architecture
In Uniswap V3, each pool has its own contract instance, which makes initializing pools and performing swaps in multiple pools costly.
Whereas, in V4, all pools are kept in a single contract to provide substantial gas savings.

![High Level Architecture](./images/01_Pool_Initialization/v3_high_level_architecture.png)

![High Level Architecture](./images/01_Pool_Initialization/v4_high_level_architecture.png)

Early calculations say that V4 will
make the gas cost of creating pools 99% less. Hooks offer unlimited choices and the single contract lets you easily
move through all these choices.

This Singleton design is improved by a new "flash accounting" method. Instead of moving assets in and out of pools
after each swap in V3, this method only moves the net balances. This means the system is a lot more efficient and
saves even more gas in Uniswap V4.

![V3 Detailed Architecture](./images/01_Pool_Initialization/v3_detailed_architecture.png)

![V4 Detailed Architecture](./images/01_Pool_Initialization/v4_detailed_architecture.png)

Because of the efficiency of the Singleton contract and flash accounting, there is no need to limit fee tiers. People
who create pools can choose them to be most competitive or change them with a dynamic fee hook. V4 also supports
native ETH again, which helps save more gas.
195 changes: 195 additions & 0 deletions docs/contracts/v4/concepts/02-2-pool-manager-and-initialization.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
id: pool-manager-and-pool-initialization
title: Pool Manager and Pool Initialization
sidebar_position: 1
---

# PoolManager
To understand the major parts of the PoolManager, let's look at the the main interface: `IPoolManager.sol`.

https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IPoolManager.sol

### Lifecycle Functions

1. **initialize**: This function initializes a new pool with specified parameters such as pool key, initial price, and
optional hook data, setting up the fundamental characteristics of the pool.

2. **swap**: Allows users to exchange one type of currency for another within a specified pool, adhering to set limits
and conditions, and adjusting the pool's state accordingly.

3. **modifyLiquidity**: Enables users to change the amount of liquidity they've provided to a pool, either by adding or
removing it, based on specified upper and lower tick limits.

### Balance Functions
1. **mint (related to ERC6909 claims)**: Used for creating new claim tokens (as per ERC6909 standards) for a user,
denoting specific rights or entitlements, but not representing liquidity provider (LP) receipt tokens.

2. **burn (related to ERC6909 claims)**: Allows users to destroy their claim tokens (compliant with ERC6909),
effectively relinquishing the rights or entitlements those tokens represented.

3. **take**: This function allows users to withdraw or "net out" a specified amount of a currency, which could be seen
as a mechanism for zero-cost flash loans under certain conditions.

4. **settle**: Used by users to pay off any outstanding amounts they owe, potentially in a different currency, with
the function returning the amount paid.

The `mint` and `burn` functions are specifically related to handling ERC6909 claims, which are distinct from liquidity
provider receipt tokens. These functions deal with specific claims or entitlements rather than the representation of a
user's share in the liquidity pool.


# Pool Initialization
The `initialize` function sets up a new liquidity pool in Uniswap. It takes necessary information such as currencies
and pricing info, and hook information as inputs, checks various conditions to ensure that the pool is set up correctly,
and sets initial values for the pool.

https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol
```solidity
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
// ... [other imports and contract definitions]
/// @notice Holds the state for all pools
contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims {
// ... [other definitions and functions]
/// @inheritdoc IPoolManager
function initialize(PoolKey memory key, uint160 sqrtPriceX96, bytes calldata hookData)
external
override
onlyByLocker // Modifier ensures only the current locker can call this function
returns (int24 tick)
{
// Check if the fee specified in the key is too large
if (key.fee.isStaticFeeTooLarge()) revert FeeTooLarge();
// Validate tick spacing - it must be within defined min and max limits
if (key.tickSpacing > MAX_TICK_SPACING) revert TickSpacingTooLarge();
if (key.tickSpacing < MIN_TICK_SPACING) revert TickSpacingTooSmall();
// Ensure the currency order is correct (currency0 < currency1)
if (key.currency0 >= key.currency1) revert CurrenciesOutOfOrderOrEqual();
// Validate the hooks contract address
if (!key.hooks.isValidHookAddress(key.fee)) revert Hooks.HookAddressNotValid(address(key.hooks));
// Call before initialization hook with provided data
key.hooks.beforeInitialize(key, sqrtPriceX96, hookData);
// Convert the PoolKey to a PoolId
PoolId id = key.toId();
// Fetch protocol fee and dynamic swap fee if applicable
(, uint16 protocolFee) = _fetchProtocolFee(key);
uint24 swapFee = key.fee.isDynamicFee() ? _fetchDynamicSwapFee(key) : key.fee.getStaticFee();
// Initialize the pool with the given parameters and receive the current tick
tick = pools[id].initialize(sqrtPriceX96, protocolFee, swapFee);
// Call after initialization hook with the resulting data
key.hooks.afterInitialize(key, sqrtPriceX96, tick, hookData);
// Emit an event to signal the initialization of the pool with key details
emit Initialize(id, key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks);
}
// ... [other functions]
}
```
Upon success, the transaction announces a new pool was created by emitting an `Initialize` event.

# PoolKey

The `PoolKey` is a structure that uniquely identifies a liquidity pool by storing its details -- the two
currencies involved (sorted numerically), the swap fee, tick spacing, and hooks (extra functionalities) of the pool.

It acts as a unique identifier, ensuring that each pool can be precisely specified and accessed within the code.

The liquidity for a PoolKey is unique to that pool alone

```solidity
/// @notice Returns the key for identifying a pool
struct PoolKey {
/// @notice The lower currency of the pool, sorted numerically
Currency currency0;
/// @notice The higher currency of the pool, sorted numerically
Currency currency1;
/// @notice The pool swap fee, capped at 1_000_000. The upper 4 bits determine if the hook sets any fees.
uint24 fee;
/// @notice Ticks that involve positions must be a multiple of tick spacing
int24 tickSpacing;
/// @notice The hooks of the pool
IHooks hooks;
}
```

Since we create and pass the `PoolKey` to the `initialize` function, and as part of the PoolKey we pass the `hooks` we
want to use for the pool.

We can use the `hooks` to customize the pool to our liking.

# Initialization Code
Here are the important parts of the initialization code from the `PoolManagerInitializeTest` contract.

https://github.com/Uniswap/v4-core/blob/main/test/PoolManagerInitialize.t.sol
```solidity
contract Deployers {
function deployFreshManager() internal {
manager = new PoolManager(500000);
}
function deployFreshManagerAndRouters() internal {
deployFreshManager();
// Initialize various routers with the deployed manager. These routers likely handle
// different aspects of the pool's functionality, such as swapping, liquidity modification, etc.
swapRouter = new PoolSwapTest(manager);
modifyLiquidityRouter = new PoolModifyLiquidityTest(manager);
donateRouter = new PoolDonateTest(manager);
takeRouter = new PoolTakeTest(manager);
initializeRouter = new PoolInitializeTest(manager); // This is the router that is used to initialize the pool
// ... [other routers]
}
}
contract PoolManagerInitializeTest is Test, Deployers, GasSnapshot {
function setUp() public {
deployFreshManagerAndRouters();
(currency0, currency1) = deployMintAndApprove2Currencies();
uninitializedKey = PoolKey({
currency0: currency0,
currency1: currency1,
fee: 3000,
hooks: IHooks(ADDRESS_ZERO),
tickSpacing: 60
});
}
function test_initialize_succeedsWithHooks(uint160 sqrtPriceX96) public {
// Assumptions tested in Pool.t.sol
sqrtPriceX96 = uint160(bound(sqrtPriceX96, TickMath.MIN_SQRT_RATIO, TickMath.MAX_SQRT_RATIO - 1));
address payable mockAddr = payable(address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG)));
address payable hookAddr = payable(MOCK_HOOKS);
vm.etch(hookAddr, vm.getDeployedCode("EmptyTestHooks.sol:EmptyTestHooks"));
MockContract mockContract = new MockContract();
vm.etch(mockAddr, address(mockContract).code);
MockContract(mockAddr).setImplementation(hookAddr);
uninitializedKey.hooks = IHooks(mockAddr);
// Call initialize function with the uninitialized key and the specified sqrtPriceX96
int24 tick = initializeRouter.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES);
(Pool.Slot0 memory slot0,,,) = manager.pools(uninitializedKey.toId());
assertEq(slot0.sqrtPriceX96, sqrtPriceX96, "sqrtPrice");
}
}
```
Loading

1 comment on commit 1a77926

@vercel
Copy link

@vercel vercel bot commented on 1a77926 Jan 11, 2024

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

docs – ./

docs-uniswap.vercel.app
docs.uniswap.org
docs-git-main-uniswap.vercel.app

Please sign in to comment.