Uniswap V4 Documentation
website·Indexed 31 days ago
https://blockchaincampus.netTechnical guides and core explanations for Uniswap V4, covering the Pool Manager, Flash Accounting via EIP-1153, and the execution of pool.swap(params). Includes deep dives into liquidity modification, pool creation, and hook examples such as Limit Orders.
What's inside Uniswap V4
- The limit order contract is designed to allow multiple users to place orders at the same price (tick). This increases the liquidity depth at that specific price level, allowing multiple liquidity providers to participate in the same limit order tick.
Understand Flash Accounting via EIP-1153 Transient Storage
Uniswap V4 uses 'Flash Accounting' to replace frequent persistent storage writes and token transfers during multi-step operations (like cross-pool swaps). It leverages EIP-1153 transient storage (TLOADandTSTOREopcodes), which is persistent only for the duration of a single transaction. This reduces gas costs significantly: transient storage operations cost 100 gas, compared to 2100 for cold reads and 2900 for dirty writes in traditional storage. Intermediate balance changes (deltas) are tracked in transient storage and settled in a single batch at the end of the transaction.Blockchain Campus co-reading workflow
The process for participating in the co-reading program follows these five sequential steps:
- Connect Wallet
- Get SUI Tokens
- Stake SUI
- Submit Review
- Verify & Claim
Understand the Epoch system in Uniswap V4 Limit Orders
The Limit Order hook uses an 'Epoch' system to uniquely identify and manage conditional orders. An Epoch is auint232identifier. Multiple users can contribute liquidity to the same price level (tick) and direction (zeroForOne), effectively sharing a single Epoch. This allows the hook to track total liquidity for a specific order and distribute the resulting tokens proportionally to each contributor based on their share of the total liquidity.type Epoch is uint232; struct EpochInfo { bool filled; // Whether the order is filled Currency currency0; // First token in the pair Currency currency1; // Second token in the pair uint256 token0Total; // Total token0 amount accumulated uint256 token1Total; // Total token1 amount accumulated uint128 liquidityTotal; // Total liquidity provided by all users mapping(address => uint128) liquidity; // Liquidity contribution per user }Understand Uniswap v4 Constant Product Formula
Uniswap v4 utilizes the constant product formula, defined asx * y = k, wherexandyrepresent the reserves of two assets in a liquidity pool andkis a constant. This formula ensures that the product of the reserves remains balanced, allowing for permissionless, non-custodial trading without the need for traditional order books.Optimize gas costs using Flash Accounting and EIP-1153
Uniswap V4 utilizes a singleton pool design combined with EIP-1153 transient storage to implement 'Flash Accounting'. Instead of performing actual token transfers for every pool crossing during a transaction, the system stores temporary data (deltas) in transient storage. The final token state is written to the blockchain only once at the end of the transaction, replacing multiple individual transfers with a single final transfer to significantly reduce gas costs.Track and reconcile token deltas using NonzeroDeltaCount
Uniswap V4 uses a transient storage counter to ensure all token deltas are reconciled by the end of a transaction. The count is incremented when a new delta is set (previous value was 0) and decremented when a delta is cleared (next value is 0). A final check ensures the count returns to 0, guaranteeing all obligations are settled.// src/libraries/NonzeroDeltaCount.sol function increment() internal { assembly ("memory-safe") { let count := tload(NONZERO_DELTA_COUNT_SLOT) count := add(count, 1) tstore(NONZERO_DELTA_COUNT_SLOT, count) } } function decrement() internal { assembly ("memory-safe") { let count := tload(NONZERO_DELTA_COUNT_SLOT) count := sub(count, 1) tstore(NONZERO_DELTA_COUNT_SLOT, count) } }Calculate final swap balance delta
The final
swapDeltais determined by comparing the swap direction (zeroForOne) against whether the user specified an exact input or output (params.amountSpecified).- If the swap is accurate for
token1(e.g., swappingtoken0fortoken1with exact output, ortoken1fortoken0with exact input), the balance delta is calculated using the calculated amount fortoken1. - Otherwise, the swap is accurate for
token0, and the delta is calculated accordingly.
if (zeroForOne != (params.amountSpecified < 0)) { swapDelta = toBalanceDelta( amountCalculated.toInt128(), (params.amountSpecified - amountSpecifiedRemaining).toInt128() ); } else { swapDelta = toBalanceDelta( (params.amountSpecified - amountSpecifiedRemaining).toInt128(), amountCalculated.toInt128() ); }- If the swap is accurate for
Modify liquidity in Uniswap v4 pools
To modify liquidity in a Uniswap v4 pool, developers use themodifyLiquidityfunction. This process requires aPoolKeyto identify the pool and aModifyLiquidityParamsstructure to define the liquidity change. Because Uniswap v4 uses a 'singleton' design, liquidity modifications must be performed within anunlockcallback to ensure atomic settlement of token balances.struct ModifyLiquidityParams { int24 tickLower; // The lower bound of the liquidity range int24 tickUpper; // The upper bound of the liquidity range int256 liquidityDelta; // The amount of liquidity to add (positive) or remove (negative) bytes32 salt; // Allows creating multiple independent positions in the same range };Handle Exact Input vs Exact Output swaps in UniswapV4
UniswapV4 distinguishes between exact input and exact output swaps using the sign of
params.amountSpecified:- Exact Output (
params.amountSpecified > 0): The user specifies the exact amount of tokens they wish to receive. The pool calculates the required input.amountCalculatedtracks tokens the user must provide (represented as a negative value), andamountSpecifiedRemainingtracks the remaining output to be sent to the user. - Exact Input (
params.amountSpecified < 0): The user provides a fixed amount of tokens. The pool calculates the output.amountCalculatedtracks tokens the user will receive (positive value), andamountSpecifiedRemainingtracks the remaining input to be provided (negative value).
- Exact Output (
Update Pool state after a swap execution
After a swap step is computed, the pool's state must be updated to reflect the new price, current tick, and liquidity. Additionally, the global fee growth tracker (feeGrowthGlobal1X128orfeeGrowthGlobal0X128) is updated based on the swap direction (zeroForOne).self.slot0 = slot0Start.setTick(result.tick).setSqrtPriceX96(result.sqrtPriceX96); // update liquidity if it changed if (self.liquidity != result.liquidity) self.liquidity = result.liquidity; // update fee growth global if (!zeroForOne) { self.feeGrowthGlobal1X128 = step.feeGrowthGlobalX128; } else { self.feeGrowthGlobal0X128 = step.feeGrowthGlobalX128; }Calculate Token Output for a Swap within a Price Range
When swapping tokens within a price range where liquidity
Lis sufficient (and not crossing ticks), the amount of tokenx(Δx) received for a given input of tokenyis calculated by the change in the inverse square root of the price:Δx = Δ(1/sqrt(P)) * LwhereΔ(1/sqrt(P)) = (1/sqrt(P_target)) - (1/sqrt(P_current)).