diff --git a/docs/contracts/v3/_category_.json b/docs/contracts/v3/_category_.json index 74d7182c6..99fed98ce 100644 --- a/docs/contracts/v3/_category_.json +++ b/docs/contracts/v3/_category_.json @@ -1,5 +1,5 @@ { "label": "V3 Protocol", "position": 2, - "collapsed": false + "collapsed": true } diff --git a/docs/contracts/v4/deprecated/_category_.json b/docs/contracts/v4/deprecated/_category_.json deleted file mode 100644 index 2d1039e02..000000000 --- a/docs/contracts/v4/deprecated/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "DEPRECATED", - "position": 1000, - "collapsed": true -} diff --git a/docs/contracts/v4/deprecated/concepts/01-intro-to-v4.mdx b/docs/contracts/v4/deprecated/concepts/01-intro-to-v4.mdx deleted file mode 100644 index c2a3d1d03..000000000 --- a/docs/contracts/v4/deprecated/concepts/01-intro-to-v4.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -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. diff --git a/docs/contracts/v4/deprecated/concepts/02-1-overview.mdx b/docs/contracts/v4/deprecated/concepts/02-1-overview.mdx deleted file mode 100644 index 3957c1f9b..000000000 --- a/docs/contracts/v4/deprecated/concepts/02-1-overview.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -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. diff --git a/docs/contracts/v4/deprecated/concepts/02-2-pool-manager-and-initialization.mdx b/docs/contracts/v4/deprecated/concepts/02-2-pool-manager-and-initialization.mdx deleted file mode 100644 index c714072d7..000000000 --- a/docs/contracts/v4/deprecated/concepts/02-2-pool-manager-and-initialization.mdx +++ /dev/null @@ -1,195 +0,0 @@ ---- -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"); - - } -} -``` diff --git a/docs/contracts/v4/deprecated/concepts/03-managing-position.mdx b/docs/contracts/v4/deprecated/concepts/03-managing-position.mdx deleted file mode 100644 index 3a7c8b2cf..000000000 --- a/docs/contracts/v4/deprecated/concepts/03-managing-position.mdx +++ /dev/null @@ -1,149 +0,0 @@ ---- -id: managing-positions -title: Managing Positions -sidebar_position: 2 ---- - -# Position -To add or remove the tokens we call the function `modifyPosition` which is defined in `PoolManager.sol` file - -```solidity -struct ModifyPositionParams { - // the lower and upper tick of the position - int24 tickLower; - int24 tickUpper; - // how to modify the liquidity - int256 liquidityDelta; -} - -/// @notice Modify the position for the given pool -function modifyPosition(PoolKey memory key, ModifyPositionParams memory params, bytes calldata hookData) -external -returns (BalanceDelta); -``` - -# Important Concepts -Some of the important concepts to understand when working with Uniswap V3 or V4 positions are: - -1. Tick -2. Tick Spacing -3. SquareRoot Price X96 -4. Liquidity Delta - -### Tick -`tick` is a measure used in this code to handle prices of two different assets (tokens) in a unique way. A `tick` -represents a specific price ratio between these two assets, calculated using a mathematical formula. - -![Price And Ticks](./images/02_Managing_Position/PriceAndTicks.png) - -There are minimum and maximum `tick` values defined within the code, ensuring that calculated prices are within a -reasonable and acceptable range. - -In Uniswap V3 (and V4), liquidity providers can provide liquidity at specific price ranges (ticks), allowing them to -concentrate their liquidity and potentially earn more fees. - -Each tick corresponds to a specific price, and not all prices are represented due to the discrete nature of the ticks. - -### Tick Spacing -The tick spacing is a parameter that determines the separation between these usable ticks, making only every -Nth tick available for liquidity provision, where `N` is the tick spacing. This is a form of quantization of -the price levels that liquidity can be provided at. - -### SquareRoot Price X96 -In Uniswap V3 (and V4), the square root price (`sqrtPriceX96`) is a key concept and a crucial part of the mathematical -calculations. It's utilized for various calculations, including determining the amount of tokens that should be moved -during a swap and the liquidity calculations within specific price ranges. - -Here’s a breakdown of what `sqrtPriceX96` represents: - -### 1. **Square Root Price:** -The price is represented as the square root of the actual price ratio of the tokens. This representation -simplifies the math, especially when it comes to calculating the amounts to be swapped, as well as the -liquidity calculations within tick ranges. - -### 2. **X96:** -The X96 suffix refers to the fixed-point format used. Uniswap V3 utilizes a 96-bit fixed-point number format. -In this representation, there is a convention to maintain high precision in calculations. The fixed-point -representation means that the actual floating-point number is multiplied by \(2^{96}\) and stored as an -integer. When reading the value, it has to be interpreted properly by dividing it by \(2^{96}\) to get the -actual value. - - -![SqrtPriceX96](./images/02_Managing_Position/SqrtPriceX96.png) - -### SqrtPriceX96 to Tick -Since both the `tick` and `sqrtPriceX96` are representations of the price, they can be converted from one to the other. - -The Uniswap V3/V4 core library provides two functions to convert between the two representations: - -https://github.com/Uniswap/v4-core/blob/main/src/libraries/TickMath.sol - -1. The function `getSqrtRatioAtTick` takes a `tick` value as an input, and it computes the square root of the price -ratio of the two assets at that specific `tick`. The result represents the price relationship between two -assets in a particular state or position. - -2. The `getTickAtSqrtRatio` function does the reverse—it takes a square root of a price ratio and calculates -the corresponding `tick`. This `tick` value represents a position or state where the assets have the given -price relationship. - -![sqrtPriceX96 to Tick](./images/02_Managing_Position/sqrtPriceX96_to_tick.png) - -### Liquidity Delta -The `liquidityDelta` is the difference between the current liquidity and the desired liquidity in a position. It can -be positive (when you're adding liquidity) or negative (when you're removing liquidity). - -Here is the code from Uniswap V3 contracts which calculates the liquidity (or liquidityDelta): - -https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/LiquidityManagement.sol - -```solidity - (uint160 sqrtPriceX96, , , , , , ) = pool.slot0(); - uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(params.tickLower); - uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(params.tickUpper); - - liquidity = LiquidityAmounts.getLiquidityForAmounts( - sqrtPriceX96, - sqrtRatioAX96, - sqrtRatioBX96, - params.amount0Desired, - params.amount1Desired - ); -``` - -# Example - Add Liquidity -Here is the code that adds liquidity to a position -```solidity -int24 tickLower = -600; -int24 tickUpper = 600; -uint256 liquidity = 1e18; - -PoolManager manager = new PoolManager(500000); -// Helpers for interacting with the pool -PoolModifyPositionTest modifyPositionRouter = new PoolModifyPositionTest(IPoolManager(address(manager))); - -modifyPositionRouter.modifyPosition( - poolKey, - IPoolManager.ModifyPositionParams({ - tickLower: tickLower, - tickUpper: tickUpper, - liquidityDelta: int256(liquidity) - }), - ZERO_BYTES -); -``` -Note: `PoolModifyPositionTest` implements the `ILockCallback` interface and adds the `lockAcquired` function, which in turn calls the `manager.modifyPosition` function. - -# Acquiring Lock -Full detail about the locking mechanism is explained in the [Locking Mechanism](/03_Locking_Mechanism/README.md) section. - -The contract that calls the `modifyPosition` must implement ILockCallback interface. - -PoolModifyPositionTest.sol has some examples of how to acquire lock and some basic checks in place. -https://github.com/Uniswap/v4-core/blob/main/src/test/PoolModifyPositionTest.sol - -In `PoolModifyPositionTest` the `lockAcquired` function is executed when the lock is acquired, handling -balance adjustments and interactions with external currencies and contracts. The function takes raw -encoded data as input, which is then decoded into structured data, specifically `CallbackData`. Essential -validations and checks are performed, ensuring the caller of the function is the manager, and it -processes the modifications such as settling amounts and making necessary transfers based on -conditions like whether the amount being positive or negative. diff --git a/docs/contracts/v4/deprecated/concepts/04-swap-tokens.mdx b/docs/contracts/v4/deprecated/concepts/04-swap-tokens.mdx deleted file mode 100644 index ecc594d8c..000000000 --- a/docs/contracts/v4/deprecated/concepts/04-swap-tokens.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -id: swap-tokens -title: Swap Tokens -sidebar_position: 3 ---- - -# Swap - Step by Step - -The swap happens in a loop until the specified amount has been completely used or the price limit -has been reached. In each iteration, the code calculates how much of the tokens can be swapped at -the current price level. - -The swap keeps iterating until either the specified amount is fully used or the square root of -the price hits the defined limit (`sqrtPriceLimitX96`). - -Here is the `SwapParams` struct, `Swap` event and `swap` function from IPoolManager which is used to swap tokens: - -```solidity -/// @notice Emitted for swaps between currency0 and currency1 -/// @param id The abi encoded hash of the pool key struct for the pool that was modified -/// @param sender The address that initiated the swap call, and that received the callback -/// @param amount0 The delta of the currency0 balance of the pool -/// @param amount1 The delta of the currency1 balance of the pool -/// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 -/// @param liquidity The liquidity of the pool after the swap -/// @param tick The log base 1.0001 of the price of the pool after the swap -event Swap( - PoolId indexed id, - address indexed sender, - int128 amount0, - int128 amount1, - uint160 sqrtPriceX96, - uint128 liquidity, - int24 tick, - uint24 fee -); - -struct SwapParams { - bool zeroForOne; - int256 amountSpecified; - uint160 sqrtPriceLimitX96; -} - -/// @notice Swap against the given pool -function swap(PoolKey memory key, SwapParams memory params, bytes calldata hookData) - external - returns (BalanceDelta); -``` - -# Example -Here is an example of how to swap tokens by calling the `swap` function: -```solidity -PoolSwapTest swapRouter = new PoolSwapTest(IPoolManager(address(manager))); - -PoolSwapTest.TestSettings memory testSettings = - PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); - -swapRouter.swap(key, params, testSettings, hookData); -``` -Note: `PoolSwapTest` implements the `ILockCallback` interface and adds the `lockAcquired` function, which in turn calls the `manager.swap` function. - -# Acquiring Lock -Full detail about the locking mechanism is explained in the [Locking Mechanism](/03_Locking_Mechanism/README.md) section. - -The contract that calls the `swap` must implement ILockCallback interface. - -`PoolSwapTest.sol` has some examples of how to acquire lock and some basic checks in place. -https://github.com/Uniswap/v4-core/blob/main/src/test/PoolSwapTest.sol - -In `PoolSwapTest`, the `lockAcquired` function is triggered when a certain "lock" is acquired in the -context of swapping assets or tokens. Once this lock is confirmed, the function handles -the settlement based on the result of the swap. It decodes the data provided to understand the context, asks -the manager to perform the swap, and then either -transfers, withdraws, or mints tokens based on the balance changes resulting from the swap. - -To ensure secure operations, the function checks that it's only being called by the intended `manager` contract. -Depending on the type of swap and settings, it handles the balance adjustments for two types of currencies: -`currency0` and `currency1`. After settling all balances, it returns the balance changes to the caller. diff --git a/docs/contracts/v4/deprecated/concepts/05-lock-mechanism.mdx b/docs/contracts/v4/deprecated/concepts/05-lock-mechanism.mdx deleted file mode 100644 index ff779db95..000000000 --- a/docs/contracts/v4/deprecated/concepts/05-lock-mechanism.mdx +++ /dev/null @@ -1,192 +0,0 @@ ---- -id: lock-mechanism -title: Lock Mechanism - Flash Accounting -sidebar_position: 4 ---- -# Intro to Locking -The locking mechanism in V4 ensures that certain operations are executed atomically without interference, ensuring -consistency and correctness in the PoolManager's state. PoolManager, uses `Lockers` to manage a queue of -lockers, ensuring that all currency deltas are settled before releasing a lock. - -Pool actions can be taken by acquiring a lock on the contract and implementing the `lockAcquired` callback to -then proceed with any of the following actions on the pools: - -- `swap` -- `modifyPosition` -- `donate` -- `take` -- `settle` -- `mint` - -# Main Components -In `PoolManager`, "locking" is essentially a way to ensure certain operations are coordinated and don't interfere -with each other. Here are the main components of the locking mechanism: - -### 1. **Locking and Unlocking:** -- The `lock` function is where the locking mechanism is initiated. It pushes an address tuple (address locker, address lockCaller) -to the locker array. - - ```solidity - function lock(address lockTarget, bytes calldata data) external payable override returns (bytes memory result) { - Lockers.push(lockTarget, msg.sender); - - // the caller does everything in this callback, including paying what they owe via calls to settle - result = ILockCallback(lockTarget).lockAcquired(msg.sender, data); - - if (Lockers.length() == 1) { - if (Lockers.nonzeroDeltaCount() != 0) revert CurrencyNotSettled(); - Lockers.clear(); - } else { - Lockers.pop(); - } - } - ``` - -- During the lock, a callback function `ILockCallback(lockTarget).lockAcquired(msg.sender, data)` is called, where the locked -contract can perform necessary operations. - -- After the operations in the callback are completed, it either clears the `Lockers` if it has only element, -signifying the release of the lock, or it pops the last element from `Lockers`, signifying that the lock is -released by that particular address. - -### 2. **Lockers.sol** - -The `Lockers` library stores an array/queue of locker addresses and their corresponding lock callers in transient storage. -Each locker is represented as a tuple (address locker, address lockCaller), and each tuple occupies two slots in -transient storage. The functions `push`, `pop`, `length`, and `clear` are used to manage this queue. - -### 3. **Non-Zero Deltas Tracking(Flash Accounting):** - -- The `Lockers` library also includes a mechanism to keep track of the number of nonzero deltas -through `nonzeroDeltaCount`, `incrementNonzeroDeltaCount`, and `decrementNonzeroDeltaCount`. These functions tracks -the number of changes or deltas that have occurred and these deltas are checked before releasing the lock. - - -- The `_accountDelta` function tracks the balance changes (delta) for each locker with respect to a specific currency. -It first checks if the delta (change in balance) is zero; if it is, the function -returns immediately as there's no change to account for. If there's a non-zero delta, the function retrieves the -current balance for the specified locker and currency, and then calculates the new balance by adding the delta -to the current balance. - - The function also uses the `Lockers` library to increment or decrement the `nonzeroDeltaCount` that tracks the number -of non-zero balance changes. - - ```solidity - function _accountDelta(Currency currency, int128 delta) internal { - if (delta == 0) return; - - address locker = Lockers.getCurrentLocker(); - int256 current = currencyDelta[locker][currency]; - int256 next = current + delta; - - unchecked { - if (next == 0) { - Lockers.decrementNonzeroDeltaCount(); - } else if (current == 0) { - Lockers.incrementNonzeroDeltaCount(); - } - } - - currencyDelta[locker][currency] = next; - } - ``` -- This mechanism is a key component of what is termed "Flash Accounting" in Uniswap V4. Flash Accounting is an -innovative approach introduced with the new singleton-style pool management. This feature fundamentally -alters the management of tokens during transaction processes. Traditional methods typically require explicit -tracking of token balances at every operational phase. In contrast, Flash Accounting operates under the principle -that by the end of each transaction or "lock period," there should be no net tokens owed either to the pool or the -caller, streamlining the accounting process significantly. - -### Working -The locking mechanism in the PoolManager contract works as follows: - -1. When a user wants to lock, they call the `lock()` function with the data that they want to be passed to the callback. -2. The `lock()` function pushes the tuple (address locker, address lockCaller) onto the locker queue. -3. The `lock()` function then calls the `ILockCallback(lockTarget).lockAcquired(msg.sender, data)` callback. -4. The callback can do whatever it needs to do, such as updating the user's balances or interacting with other contracts. -5. Once the callback is finished, it returns to the `lock()` function. -6. The `lock()` function checks if there are any other lockers in the queue. If there are, it pops the next locker off the queue and calls the callback for that locker. -7. If there are no more lockers in the queue, the `lock()` function returns. - -# Example -Below is the example from a community ["Liquidity Bootstrapping Hook"](https://github.com/kadenzipfel/uni-lbp/blob/main/src/LiquidityBootstrappingHooks.sol) hook that uses the locking mechanism and calls the `modifyPosition` and `swap` functions. - -```solidity -/// @notice Callback function called by the poolManager when a lock is acquired -/// Used for modifying positions and swapping tokens internally -/// @param data Data passed to the lock function -/// @return Balance delta -function lockAcquired(bytes calldata data) external override poolManagerOnly returns (bytes memory) { - bytes4 selector = abi.decode(data[:32], (bytes4)); - - if (selector == IPoolManager.modifyPosition.selector) { - ModifyPositionCallback memory callback = abi.decode(data[32:], (ModifyPositionCallback)); - - BalanceDelta delta = poolManager.modifyPosition(callback.key, callback.params, bytes("")); - - if (callback.params.liquidityDelta < 0) { - // Removing liquidity, take tokens from the poolManager - _takeDeltas(callback.key, delta, callback.takeToOwner); // Take to owner if specified (exit) - } else { - // Adding liquidity, settle tokens to the poolManager - _settleDeltas(callback.key, delta); - } - - return abi.encode(delta); - } - - if (selector == IPoolManager.swap.selector) { - SwapCallback memory callback = abi.decode(data[32:], (SwapCallback)); - - BalanceDelta delta = poolManager.swap(callback.key, callback.params, bytes("")); - - // Take and settle deltas - _takeDeltas(callback.key, delta, true); // Take tokens to the owner - _settleDeltas(callback.key, delta); - - return abi.encode(delta); - } - - return bytes(""); -} -``` - -Other important thing to note is that before the lock is released, the `nonzeroDeltaCount` is checked to ensure that -all currency deltas are settled. This is done by `_takeDeltas` and `_settleDeltas` functions. - -```solidity -/// @notice Helper function to take tokens according to balance deltas -/// @param delta Balance delta -/// @param takeToOwner Whether to take the tokens to the owner -function _takeDeltas(PoolKey memory key, BalanceDelta delta, bool takeToOwner) internal { - PoolId poolId = key.toId(); - int256 delta0 = delta.amount0(); - int256 delta1 = delta.amount1(); - - if (delta0 < 0) { - poolManager.take(key.currency0, takeToOwner ? owner[poolId] : address(this), uint256(-delta0)); - } - - if (delta1 < 0) { - poolManager.take(key.currency1, takeToOwner ? owner[poolId] : address(this), uint256(-delta1)); - } -} - -/// @notice Helper function to settle tokens according to balance deltas -/// @param key Pool key -/// @param delta Balance delta -function _settleDeltas(PoolKey memory key, BalanceDelta delta) internal { - int256 delta0 = delta.amount0(); - int256 delta1 = delta.amount1(); - - if (delta0 > 0) { - key.currency0.transfer(address(poolManager), uint256(delta0)); - poolManager.settle(key.currency0); - } - - if (delta1 > 0) { - key.currency1.transfer(address(poolManager), uint256(delta1)); - poolManager.settle(key.currency1); - } -} -``` diff --git a/docs/contracts/v4/deprecated/concepts/07-deployment.mdx b/docs/contracts/v4/deprecated/concepts/07-deployment.mdx deleted file mode 100644 index efa725cd8..000000000 --- a/docs/contracts/v4/deprecated/concepts/07-deployment.mdx +++ /dev/null @@ -1,196 +0,0 @@ ---- -id: hook-deployment -title: Hook Deployment -sidebar_position: 6 ---- - -# Hook Deployment - -Each hook is associated with a specific flag, represented as a constant within the contract. These constants are -bit positions in an address. For instance, the `BEFORE_INITIALIZE_FLAG` is represented by a bit shift of `1 << 159`, -indicating it corresponds to the 160th bit in the address. When a hooks contract is deployed, its address's leading -bits are inspected to determine which hooks are enabled. - -The PoolManager, during initialization, calls the Hooks library to verify if the hooks are deployed at the correct addresses. - -For example, a hooks contract deployed at the address `0x9000000000000000000000000000000000000000` has leading bits -'1001'. This configuration activates the hooks corresponding to these bits, such as the 'before initialize' and 'after -add liquidity' hooks. This approach provides a compact and efficient method for encoding permissions directly into -the contract's address. - -This encoding indicates that two specific hooks ('before initialize' and 'after add liquidity') will be triggered. In the provided address: -- The leading `1` at the second-highest position aligns with the `BEFORE_INITIALIZE_FLAG` (bit 159), and -- The trailing `1` in the sequence `1001` aligns with the `AFTER_ADD_LIQUIDITY_FLAG` (bit 156). - -The other two `0`s in the sequence indicate that the `AFTER_INITIALIZE_FLAG` and `BEFORE_ADD_LIQUIDITY_FLAG` are not set. - - -| Hex Hook Address | Binary Address | Description | -|---------------------------------------------|---------------------|---------------------------| -| `0x8000000000000000000000000000000000000000` | `1000 0000...` (bit 159) | BEFORE_INITIALIZE_FLAG | -| `0x4000000000000000000000000000000000000000` | `0100 0000...` (bit 158) | AFTER_INITIALIZE_FLAG | -| `0x2000000000000000000000000000000000000000` | `0010 0000...` (bit 157) | BEFORE_ADD_LIQUIDITY_FLAG | -| `0x1000000000000000000000000000000000000000` | `0001 0000...` (bit 156) | AFTER_ADD_LIQUIDITY_FLAG | -| `0x0800000000000000000000000000000000000000` | `0000 1000...` (bit 155) | BEFORE_REMOVE_LIQUIDITY_FLAG| -| `0x0400000000000000000000000000000000000000` | `0000 0100...` (bit 154) | AFTER_REMOVE_LIQUIDITY_FLAG | -| `0x0200000000000000000000000000000000000000` | `0000 0010...` (bit 153) | BEFORE_SWAP_FLAG | -| `0x0100000000000000000000000000000000000000` | `0000 0001...` (bit 152) | AFTER_SWAP_FLAG | -| `0x0080000000000000000000000000000000000000` | `0000 0000 1000...` (bit 151) | BEFORE_DONATE_FLAG | -| `0x0040000000000000000000000000000000000000` | `0000 0000 0100...` (bit 150) | AFTER_DONATE_FLAG | -| `0x0020000000000000000000000000000000000000` | `0000 0000 0010...` (bit 149) | NO_OP_FLAG | -| `0x0010000000000000000000000000000000000000` | `0000 0000 0001...` (bit 148) | ACCESS_LOCK_FLAG | - - -To generate valid hook addresses based on the code provided, we focus on the leading bits that indicate -which hooks are invoked. Each flag corresponds to specific leading bits in the address, as indicated by -the constants provided. - - -Here are some example addresses based on the flags: - - -### 1. One Hook -#### **Example 1: Just BEFORE_SWAP_FLAG** -- Address: `0x2000000000000000000000000000000000000000` -- Leading bits: '0010...' -- Explanation: The flag for "before swap" is set by having a '1' in the 153rd bit (from the right), represented by `0x2000000000000000000000000000000000000000` in hexadecimal. - -#### **Example 2: Just AFTER_DONATE_FLAG** -- Address: `0x4000000000000000000000000000000000000000` -- Leading bits: '0100...' -- Explanation: The flag for "after donate" is set by having a '1' in the 150th bit, indicated by `0x4000000000000000000000000000000000000000`. - -### 2. Two Hooks -#### **Example 3: BEFORE_SWAP_FLAG and AFTER_SWAP_FLAG** -- Address: `0x3000000000000000000000000000000000000000` -- Leading bits: '0011...' -- Explanation: Combining flags for "before swap" and "after swap" requires setting bits 153 and 152, resulting in `0x3000000000000000000000000000000000000000`. - -#### **Example 4: BEFORE_INITIALIZE_FLAG and AFTER_INITIALIZE_FLAG** -- Address: `0xC000000000000000000000000000000000000000` -- Leading bits: '1100...' -- Explanation: The combination of "before initialize" and "after initialize" sets bits 159 and 158, represented by `0xC000000000000000000000000000000000000000`. - - -```solidity -library Hooks { - // These flags are defined using bitwise left shifts. The `1 << n` operation means that the binary number `1` is shifted - // to the left by `n` positions, effectively placing a `1` at the `n`th bit position (counting from the right and - // starting from 0). This technique is commonly used in programming to set a specific bit in a number, which can be used - // as a flag. In Ethereum addresses, which are 160 bits long, these flags correspond to the leading bits because of the - // high positions of the shift (e.g., 159, 158). - - uint256 internal constant BEFORE_INITIALIZE_FLAG = 1 << 159; // (Bit 159) - uint256 internal constant AFTER_INITIALIZE_FLAG = 1 << 158; // (Bit 158) - uint256 internal constant BEFORE_ADD_LIQUIDITY_FLAG = 1 << 157; // (Bit 157) - uint256 internal constant AFTER_ADD_LIQUIDITY_FLAG = 1 << 156; // (Bit 156) - uint256 internal constant BEFORE_REMOVE_LIQUIDITY_FLAG = 1 << 155; // (Bit 155) - uint256 internal constant AFTER_REMOVE_LIQUIDITY_FLAG = 1 << 154; // (Bit 154) - uint256 internal constant BEFORE_SWAP_FLAG = 1 << 153; // (Bit 153) - uint256 internal constant AFTER_SWAP_FLAG = 1 << 152; // (Bit 152) - uint256 internal constant BEFORE_DONATE_FLAG = 1 << 151; // (Bit 151) - uint256 internal constant AFTER_DONATE_FLAG = 1 << 150; // (Bit 150) - uint256 internal constant NO_OP_FLAG = 1 << 149; // (Bit 149) - uint256 internal constant ACCESS_LOCK_FLAG = 1 << 148; // (Bit 148) - - - /// @notice Utility function intended to be used in hook constructors to ensure - /// the deployed hooks address causes the intended hooks to be called - /// @param permissions The hooks that are intended to be called - /// @dev permissions param is memory as the function will be called from constructors - function validateHookPermissions(IHooks self, Permissions memory permissions) internal pure { - if ( - permissions.beforeInitialize != self.hasPermission(BEFORE_INITIALIZE_FLAG) - || permissions.afterInitialize != self.hasPermission(AFTER_INITIALIZE_FLAG) - || permissions.beforeAddLiquidity != self.hasPermission(BEFORE_ADD_LIQUIDITY_FLAG) - || permissions.afterAddLiquidity != self.hasPermission(AFTER_ADD_LIQUIDITY_FLAG) - || permissions.beforeRemoveLiquidity != self.hasPermission(BEFORE_REMOVE_LIQUIDITY_FLAG) - || permissions.afterRemoveLiquidity != self.hasPermission(AFTER_REMOVE_LIQUIDITY_FLAG) - || permissions.beforeSwap != self.hasPermission(BEFORE_SWAP_FLAG) - || permissions.afterSwap != self.hasPermission(AFTER_SWAP_FLAG) - || permissions.beforeDonate != self.hasPermission(BEFORE_DONATE_FLAG) - || permissions.afterDonate != self.hasPermission(AFTER_DONATE_FLAG) - || permissions.noOp != self.hasPermission(NO_OP_FLAG) - || permissions.accessLock != self.hasPermission(ACCESS_LOCK_FLAG) - ) { - revert HookAddressNotValid(address(self)); - } - } - - function hasPermission(IHooks self, uint256 flag) internal pure returns (bool) { - return uint256(uint160(address(self))) & flag != 0; - } -} -``` - - - -# CREATE2 -Ethereum blockchain allows you to create contracts. There are two ways to create -these contracts: - -1. **CREATE**: This is the regular way. Every time you create a contract using this, it gets a new, unique -address (like a house getting a unique postal address). - -2. **CREATE2**: This is a advanced way. Here, you use your address, a `salt` which is a unique number you choose, and -the contract's code called `bytecode` to create the contract. The magic of `CREATE2` is that if you use the same -fields, you'll get the same contract address every time. - -Using `CREATE2` helps ensure that the hook is deployed to the exact right address. - -Here's a small code that predicts the address where a contract will be deployed using `CREATE2` before actually -deploying it. -```solidity -bytes32 salt = keccak256(abi.encodePacked(someData)); -address predictedAddress = address(uint(keccak256(abi.encodePacked( - byte(0xff), - deployerAddress, - salt, - keccak256(bytecode) -)))); -``` - - -# Deterministic Deployment Proxy -Many developers use https://github.com/Arachnid/deterministic-deployment-proxy to deploy contracts to a specific -address. The main feature of this project is the use of the Ethereum CREATE2 opcode, which allows for deterministic -deployment of contracts. The deployment proxy also enables the same address across different networks. - -Most of the chains do have the deployment proxy at `0x4e59b44847b379578588920cA78FbF26c0B4956C`. See [here](https://github.com/Uniswap/v4-periphery/issues/59#issuecomment-1716379675) -for more details. - -# Hook Deployment Code -The https://github.com/uniswapfoundation/v4-template repository contains some helper utilities for deploying hooks. - -Here is the code for deploying the hooks using Deterministic Deployment Proxy which is deployed at `0x4e59b44847b379578588920cA78FbF26c0B4956C`: -```solidity -contract CounterScript is Script { - address constant CREATE2_DEPLOYER = address(0x4e59b44847b379578588920cA78FbF26c0B4956C); - address constant GOERLI_POOLMANAGER = address(0x3A9D48AB9751398BbFa63ad67599Bb04e4BdF98b); - - function setUp() public {} - - function run() public { - // hook contracts must have specific flags encoded in the address - uint160 flags = uint160( - Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG - | Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG - ); - - // Mine a salt that will produce a hook address with the correct flags - (address hookAddress, bytes32 salt) = - HookMiner.find(CREATE2_DEPLOYER, flags, type(Counter).creationCode, abi.encode(address(GOERLI_POOLMANAGER))); - - // Deploy the hook using CREATE2 - vm.broadcast(); - Counter counter = new Counter{salt: salt}(IPoolManager(address(GOERLI_POOLMANAGER))); - require(address(counter) == hookAddress, "CounterScript: hook address mismatch"); - } -} -``` -https://github.com/uniswapfoundation/v4-template/blob/main/script/CounterDeploy.s.sol - -Note: This is a Foundry script, and it won't work for hardhat. - - -Read more about deploying your own hooks [here](/contracts/v4/first-hook/hook-deployment). diff --git a/docs/contracts/v4/deprecated/concepts/_category_.json b/docs/contracts/v4/deprecated/concepts/_category_.json deleted file mode 100644 index 74b831d11..000000000 --- a/docs/contracts/v4/deprecated/concepts/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Concepts", - "position": 2, - "collapsed": false -} diff --git a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/HighLevelArchitecture.png b/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/HighLevelArchitecture.png deleted file mode 100644 index 84d466c9a..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/HighLevelArchitecture.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v3_detailed_architecture.png b/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v3_detailed_architecture.png deleted file mode 100644 index 3001b623f..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v3_detailed_architecture.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v3_high_level_architecture.png b/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v3_high_level_architecture.png deleted file mode 100644 index 2bd471a1b..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v3_high_level_architecture.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v4_detailed_architecture.png b/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v4_detailed_architecture.png deleted file mode 100644 index 6e9364852..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v4_detailed_architecture.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v4_high_level_architecture.png b/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v4_high_level_architecture.png deleted file mode 100644 index acbeb32b0..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/01_Pool_Initialization/v4_high_level_architecture.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/02_Managing_Position/PriceAndTicks.png b/docs/contracts/v4/deprecated/concepts/images/02_Managing_Position/PriceAndTicks.png deleted file mode 100644 index 65755db54..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/02_Managing_Position/PriceAndTicks.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/02_Managing_Position/SqrtPriceX96.png b/docs/contracts/v4/deprecated/concepts/images/02_Managing_Position/SqrtPriceX96.png deleted file mode 100644 index 87fd7764e..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/02_Managing_Position/SqrtPriceX96.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/02_Managing_Position/sqrtPriceX96_to_tick.png b/docs/contracts/v4/deprecated/concepts/images/02_Managing_Position/sqrtPriceX96_to_tick.png deleted file mode 100644 index f4fb57d76..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/02_Managing_Position/sqrtPriceX96_to_tick.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/03_fee_calculation/FeeCalculation.png b/docs/contracts/v4/deprecated/concepts/images/03_fee_calculation/FeeCalculation.png deleted file mode 100644 index b3465bd19..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/03_fee_calculation/FeeCalculation.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/05_locking_mechanism/LockingMechanism_excali.png b/docs/contracts/v4/deprecated/concepts/images/05_locking_mechanism/LockingMechanism_excali.png deleted file mode 100644 index 3f09b44a7..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/05_locking_mechanism/LockingMechanism_excali.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/05_locking_mechanism/locking_mechanism.png b/docs/contracts/v4/deprecated/concepts/images/05_locking_mechanism/locking_mechanism.png deleted file mode 100644 index e502cd172..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/05_locking_mechanism/locking_mechanism.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/concepts/images/Liquidity1.png b/docs/contracts/v4/deprecated/concepts/images/Liquidity1.png deleted file mode 100644 index 719d0af81..000000000 Binary files a/docs/contracts/v4/deprecated/concepts/images/Liquidity1.png and /dev/null differ diff --git a/docs/contracts/v4/deprecated/first-hook/01-building-you-hook.mdx b/docs/contracts/v4/deprecated/first-hook/01-building-you-hook.mdx deleted file mode 100644 index dbb282c58..000000000 --- a/docs/contracts/v4/deprecated/first-hook/01-building-you-hook.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -id: building-your-own-hook -title: Building your own hook -sidebar_position: 1 ---- - -# Build your own hook -Whenever starting on a new project, it is always a good idea to use a framework which can assist in development and -that can make the setup process easier. Similarly, when building your own hooks, will would want to use a few utilities to -1) Mining the correct salt for your hook address -2) Using a router which acquire the lock and then call various functions on the pool manager -3) Deploying the hook to a deterministic address - -Many of these steps are already taken care by a few template repositories which are listed [here](https://uniswaphooks.com/components/hooks/templates) - -## Hooks Template -One of the most popular templates is the [v4-template](https://github.com/uniswapfoundation/v4-template) which is -created by [Sauce](https://github.com/saucepoint). - -You can clone the repository and then follow the instructions in the README to get started. - -## Getting Started -1. **Installation**: The template requires Foundry. Install dependencies using: -```shell -forge install -``` -2. **Testing**: Run tests with: -```shell -forge test -``` -## Counter Hook -The template includes an example hook (`Counter.sol`), a test for it, and scripts for deploying hooks. - -Here are some of the things to note about the Counter hook: - -1. It extends the `BaseHook` contract which is defined in the [v4-periphery](https://github.com/Uniswap/v4-periphery/blob/main/contracts/BaseHook.sol) repository. -```solidity -import {BaseHook} from "v4-periphery/BaseHook.sol"; - -contract Counter is BaseHook { - // ... hook code here -} - -``` -2. The hook gets called before and after every swap and modify position call. This is done as part of `getHookPermissions` function - -```solidity -function getHookPermissions() public pure override returns (Hooks.Permissions memory) { - return Hooks.Permissions({ - beforeInitialize: false, - afterInitialize: false, - beforeAddLiquidity: true, - afterAddLiquidity: false, - beforeRemoveLiquidity: true, - afterRemoveLiquidity: false, - beforeSwap: true, - afterSwap: true, - beforeDonate: false, - afterDonate: false, - noOp: false, - accessLock: false - }); -} - -``` - -3. Corresponding to the hooks calls, the hook implements the following functions: -```solidity - function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata) - external - override - returns (bytes4) - { - beforeSwapCount[key.toId()]++; - return BaseHook.beforeSwap.selector; - } - - function afterSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata) - external - override - returns (bytes4) - { - afterSwapCount[key.toId()]++; - return BaseHook.afterSwap.selector; - } - - function beforeAddLiquidity( - address, - PoolKey calldata key, - IPoolManager.ModifyLiquidityParams calldata, - bytes calldata - ) external override returns (bytes4) { - beforeAddLiquidityCount[key.toId()]++; - return BaseHook.beforeAddLiquidity.selector; - } - - function beforeRemoveLiquidity( - address, - PoolKey calldata key, - IPoolManager.ModifyLiquidityParams calldata, - bytes calldata - ) external override returns (bytes4) { - beforeRemoveLiquidityCount[key.toId()]++; - return BaseHook.beforeRemoveLiquidity.selector; - } -``` -4. In each of these functions, Counter hook increments a counter. You can see this in the `beforeSwap` function: -```solidity -function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata) - external - override - returns (bytes4) -{ - beforeSwapCount[key.toId()]++; - return BaseHook.beforeSwap.selector; -} -``` - -## Additional Resources -- [v4-periphery](https://github.com/Uniswap/v4-periphery): For advanced hook implementations. -- [v4-core](https://github.com/Uniswap/v4-core): The core repository for Uniswap v4. - - - - diff --git a/docs/contracts/v4/deprecated/first-hook/02-hook-testing.mdx b/docs/contracts/v4/deprecated/first-hook/02-hook-testing.mdx deleted file mode 100644 index a63e5acfa..000000000 --- a/docs/contracts/v4/deprecated/first-hook/02-hook-testing.mdx +++ /dev/null @@ -1,102 +0,0 @@ ---- -id: testing-hooks -title: Testing Hooks -sidebar_position: 2 ---- - -## Testing hooks -Testing hooks is same as testing contracts. The template includes a test for the Counter hook, which you can find in `test/Counter.t.sol`. - -Here are some key points about the Counter hook test: -1. The hook extends from a couple of utilities that facilitate easier testing of hooks. - - ```solidity - import "forge-std/Test.sol"; - import {Deployers} from "v4-core/test/utils/Deployers.sol"; - - contract CounterTest is Test, Deployers { - } - ``` - -2. The `setup` function, called before every test, creates a few test tokens, retrieves the hook address, and then initializes the pool with this hook address. - - ```solidity - function setup() public { - Deployers.deployFreshManagerAndRouters(); - (currency0, currency1) = Deployers.deployMintAndApprove2Currencies(); - - // Deploy the hook to an address with the correct flags - uint160 flags = uint160( - Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG - | Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG - ); - (address hookAddress, bytes32 salt) = - HookMiner.find(address(this), flags, type(Counter).creationCode, abi.encode(address(manager))); - counter = new Counter{salt: salt}(IPoolManager(address(manager))); - } - ``` - - Pool is then initialized containing this hook - ```solidity - // Create the pool - key = PoolKey(currency0, currency1, 3000, 60, IHooks(counter)); - poolId = key.toId(); - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - ``` - -3. Hook tests utilize a router, namely `PoolModifyPositionTest`, to modify positions. PoolModifyPositionTest implements the `ILockCallback` interface and adds the `lockAcquired` function, which in turn calls the `manager.modifyPosition` function. - ```solidity - PoolManager manager; - PoolModifyPositionTest modifyPositionRouter; - - manager = new PoolManager(500000); - - // Helpers for interacting with the pool - modifyPositionRouter = new PoolModifyPositionTest(IPoolManager(address(manager))); - - modifyPositionRouter.modifyPosition(poolKey, IPoolManager.ModifyPositionParams(-120, 120, 10 ether), ZERO_BYTES); - ``` - Similarly, for token swaps, the test uses `PoolSwapTest`, which also implements the `ILockCallback` interface. - -4. Testing the hook closely resembles testing any other smart contract. The function `testCounterHooks` executes swaps and verifies if the counters are updated correctly. - - ```solidity - function testCounterHooks() public { - // positions were created in setup() - assertEq(counter.beforeAddLiquidityCount(poolId), 3); - assertEq(counter.beforeRemoveLiquidityCount(poolId), 0); - - assertEq(counter.beforeSwapCount(poolId), 0); - assertEq(counter.afterSwapCount(poolId), 0); - - // Perform a test swap // - bool zeroForOne = true; - int256 amountSpecified = 1e18; - BalanceDelta swapDelta = swap(key, zeroForOne, amountSpecified, ZERO_BYTES); - // ------------------- // - - assertEq(int256(swapDelta.amount0()), amountSpecified); - - assertEq(counter.beforeSwapCount(poolId), 1); - assertEq(counter.afterSwapCount(poolId), 1); - } - - /// Test Helper - function swap( - PoolKey memory key, - bool zeroForOne, - int256 amountSpecified, - bytes memory hookData - ) internal returns (BalanceDelta delta) { - IPoolManager.SwapParams memory params = IPoolManager.SwapParams({ - zeroForOne: zeroForOne, - amountSpecified: amountSpecified, - sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1 // unlimited impact - }); - - PoolSwapTest.TestSettings memory testSettings = - PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); - - delta = swapRouter.swap(key, params, testSettings, hookData); - } - ``` diff --git a/docs/contracts/v4/deprecated/first-hook/03-hook-deployment.mdx b/docs/contracts/v4/deprecated/first-hook/03-hook-deployment.mdx deleted file mode 100644 index 1aa83aa6c..000000000 --- a/docs/contracts/v4/deprecated/first-hook/03-hook-deployment.mdx +++ /dev/null @@ -1,122 +0,0 @@ ---- -id: hook-deployment -title: Hook Deployment -sidebar_position: 3 ---- - -# Deployment -Deploying Uniswap V4 Hooks involves several steps: - -1. **Deploying the PoolManager Contract**: This contract is typically pre-deployed on many test environments. However, - you have the option to deploy it locally on your machine if required. - -2. **Deploying the Hook Contract**: The hook contract needs to be deployed at a predetermined address. You can use - `CREATE2` for deterministic deployment. A Deterministic Deployment Proxy, usually found - at `0x4e59b44847b379578588920cA78FbF26c0B4956C`, is employed for this purpose and is already available in most - environments. - -3. **Deploying Test Tokens**: These tokens are essential for creating the pool. They need to be deployed before - initializing the pool. - -4. **Initializing the Pool with the Hook Contract Address**: This is achieved by invoking - the `initialize(PoolKey memory key, uint160 sqrtPriceX96, bytes calldata hookData)` function on the PoolManager contract. - -5. **Adding Liquidity or Modifying Position**: If you wish to add liquidity to the pool or alter its position, a - utility contract that implements the `ILockCallback` interface is necessary. You may consider deploying a utility - contract like `PoolModifyPositionTest` for these operations. - - -## Deployment Scripts -The template includes a few scripts that help with deploying hooks. These scripts are located in the `scripts` folder. - -Lets look at these scripts one by one: - -### 1. Deploying Your Own Tokens -The template includes Mock UNI and Mock USDC contracts for testing. Deploy them using: -```shell - forge create script/mocks/mUNI.sol:MockUNI \ - --rpc-url [your_rpc_url_here] \ - --private-key [your_private_key_on_goerli_here] \ - - forge create script/mocks/mUSDC.sol:MockUSDC \ - --rpc-url [your_rpc_url_here] \ - --private-key [your_private_key_on_goerli_here] \ -``` - - -### 2. script/01_CreatePool.s.sol -This script contains the steps for initializing the pool with an existing hook. It uses the pre-deployed PoolManager contract and -token contracts -```solidity -contract CreatePoolScript is Script { - using CurrencyLibrary for Currency; - - //addresses with contracts deployed - address constant GOERLI_POOLMANAGER = address(0x3A9D48AB9751398BbFa63ad67599Bb04e4BdF98b); //pool manager deployed to GOERLI - address constant MUNI_ADDRESS = address(0xbD97BF168FA913607b996fab823F88610DCF7737); //mUNI deployed to GOERLI -- insert your own contract address here - address constant MUSDC_ADDRESS = address(0xa468864e673a807572598AB6208E49323484c6bF); //mUSDC deployed to GOERLI -- insert your own contract address here - address constant HOOK_ADDRESS = address(0x3CA2cD9f71104a6e1b67822454c725FcaeE35fF6); //address of the hook contract deployed to goerli -- you can use this hook address or deploy your own! - - IPoolManager manager = IPoolManager(GOERLI_POOLMANAGER); - - function run() external { - // sort the tokens! - address token0 = uint160(MUSDC_ADDRESS) < uint160(MUNI_ADDRESS) ? MUSDC_ADDRESS : MUNI_ADDRESS; - address token1 = uint160(MUSDC_ADDRESS) < uint160(MUNI_ADDRESS) ? MUNI_ADDRESS : MUSDC_ADDRESS; - uint24 swapFee = 4000; - int24 tickSpacing = 10; - - // floor(sqrt(1) * 2^96) - uint160 startingPrice = 79228162514264337593543950336; - - bytes memory hookData = abi.encode(block.timestamp); - - PoolKey memory pool = PoolKey({ - currency0: Currency.wrap(token0), - currency1: Currency.wrap(token1), - fee: swapFee, - tickSpacing: tickSpacing, - hooks: IHooks(HOOK_ADDRESS) - }); - - // Turn the Pool into an ID so you can use it for modifying positions, swapping, etc. - PoolId id = PoolIdLibrary.toId(pool); - bytes32 idBytes = PoolId.unwrap(id); - - console.log("Pool ID Below"); - console.logBytes32(bytes32(idBytes)); - - vm.broadcast(); - manager.initialize(pool, startingPrice, hookData); - } -} -``` -### 3. script/00_Counter.s.sol -This script deploys the Counter hook using Deterministic Deployment Proxy. It uses the pre-deployed PoolManager contract -and proxy - -```solidity -contract CounterScript is Script { - address constant CREATE2_DEPLOYER = address(0x4e59b44847b379578588920cA78FbF26c0B4956C); - address constant GOERLI_POOLMANAGER = address(0x3A9D48AB9751398BbFa63ad67599Bb04e4BdF98b); - - function setUp() public {} - - function run() public { - // hook contracts must have specific flags encoded in the address - uint160 flags = uint160( - Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG - | Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG - ); - - // Mine a salt that will produce a hook address with the correct flags - (address hookAddress, bytes32 salt) = - HookMiner.find(CREATE2_DEPLOYER, flags, type(Counter).creationCode, abi.encode(address(GOERLI_POOLMANAGER))); - - // Deploy the hook using CREATE2 - vm.broadcast(); - Counter counter = new Counter{salt: salt}(IPoolManager(address(GOERLI_POOLMANAGER))); - require(address(counter) == hookAddress, "CounterScript: hook address mismatch"); - } -} -``` diff --git a/docs/contracts/v4/deprecated/first-hook/_category_.json b/docs/contracts/v4/deprecated/first-hook/_category_.json deleted file mode 100644 index 2b5931edd..000000000 --- a/docs/contracts/v4/deprecated/first-hook/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "My First Hook", - "position": 3, - "collapsed": false -} diff --git a/docs/contracts/v4/deprecated/overview.mdx b/docs/contracts/v4/deprecated/overview.mdx deleted file mode 100644 index a7a3af079..000000000 --- a/docs/contracts/v4/deprecated/overview.mdx +++ /dev/null @@ -1,22 +0,0 @@ -# Uniswap V4 - Hooks - -Welcome to the Uniswap V4 developer documentation. - -Uniswap V4 is designed to enhance the way liquidity is provided and tokens are traded on-chain. It aims to broaden the -scope of decentralized trading. The project welcomes community participation, providing a platform for developers and -enthusiasts to contribute to this new version of the Uniswap Protocol. - -A significant feature in Uniswap V4 is the introduction of "hooks." These contracts operate at different stages in a -pool action's lifecycle, allowing for a high degree of customization. - -Pool creators have the option to maintain the tradeoff decisions available in V3 or to explore new features. -Uniswap V4 pools can support dynamic fees, on-chain limit orders, or act as a time-weighted average market -maker (TWAMM) to spread large orders over time. The architecture has been updated, including a new "singleton" -contract where all pools are contained within a single smart contract, enhancing the platform's efficiency and -reducing costs. - -The integration of hooks with the singleton architecture is expected to make the platform more powerful and -versatile, ensuring fast, secure, and efficient customization and routing across various pools. - -Note: V4 is currently in development, and changes are ongoing in the contracts. Updates to the documentation -will be provided accordingly. diff --git a/docs/contracts/v4/guides/01-create-pool.mdx b/docs/contracts/v4/guides/01-create-pool.mdx index c39b5c2ea..4ce142c22 100644 --- a/docs/contracts/v4/guides/01-create-pool.mdx +++ b/docs/contracts/v4/guides/01-create-pool.mdx @@ -2,6 +2,6 @@ title: Create Pool --- -(TODO: document manager.initialize() and its parameters) +## 🚧 Under construction 🚧 -(TODO: mention multicall posm.intializePool) \ No newline at end of file +Please see https://www.v4-by-example.org/initialize diff --git a/docs/contracts/v4/guides/03-swap.mdx b/docs/contracts/v4/guides/03-swap.mdx index 8101315b6..d7bc6f020 100644 --- a/docs/contracts/v4/guides/03-swap.mdx +++ b/docs/contracts/v4/guides/03-swap.mdx @@ -2,8 +2,6 @@ title: Swap --- -(TODO: reference universal router?) +## 🚧 Under construction 🚧 -# Single Swap - -# Multihop Swap \ No newline at end of file +Please see https://www.v4-by-example.org/swap \ No newline at end of file diff --git a/docs/contracts/v4/guides/04-hooks/swap.mdx b/docs/contracts/v4/guides/04-hooks/01-swap.mdx similarity index 100% rename from docs/contracts/v4/guides/04-hooks/swap.mdx rename to docs/contracts/v4/guides/04-hooks/01-swap.mdx diff --git a/docs/contracts/v4/guides/04-hooks/liquidity.mdx b/docs/contracts/v4/guides/04-hooks/02-liquidity.mdx similarity index 100% rename from docs/contracts/v4/guides/04-hooks/liquidity.mdx rename to docs/contracts/v4/guides/04-hooks/02-liquidity.mdx diff --git a/docs/contracts/v4/guides/04-hooks/02-NoOp-hooks.mdx b/docs/contracts/v4/guides/04-hooks/03-NoOp.mdx similarity index 100% rename from docs/contracts/v4/guides/04-hooks/02-NoOp-hooks.mdx rename to docs/contracts/v4/guides/04-hooks/03-NoOp.mdx diff --git a/docs/contracts/v4/guides/04-hooks/01-Volatility-based-dynamic-fee-hook.mdx b/docs/contracts/v4/guides/04-hooks/04-Volatility-fee-hook.mdx similarity index 100% rename from docs/contracts/v4/guides/04-hooks/01-Volatility-based-dynamic-fee-hook.mdx rename to docs/contracts/v4/guides/04-hooks/04-Volatility-fee-hook.mdx diff --git a/docs/contracts/v4/guides/07-read-pool-state.mdx b/docs/contracts/v4/guides/07-read-pool-state.mdx index e56adda89..9c29a4cd1 100644 --- a/docs/contracts/v4/guides/07-read-pool-state.mdx +++ b/docs/contracts/v4/guides/07-read-pool-state.mdx @@ -2,4 +2,8 @@ title: Reading Pool State --- -(TODO: explain StateLibrary and TransientStateLibrary) \ No newline at end of file +## 🚧 Under construction 🚧 + +Please see [StateLibrary](https://github.com/Uniswap/v4-core/blob/main/src/libraries/StateLibrary.sol) + +and how it's used in [tests](https://github.com/Uniswap/v4-core/blob/main/test/PoolManagerInitialize.t.sol#L91) \ No newline at end of file diff --git a/docs/contracts/v4/reference/core/libraries/state-library.mdx b/docs/contracts/v4/reference/core/libraries/state-library.mdx deleted file mode 100644 index bb121d874..000000000 --- a/docs/contracts/v4/reference/core/libraries/state-library.mdx +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: StateLibrary ---- - -(TODO: state library) \ No newline at end of file diff --git a/docs/contracts/v4/reference/periphery/libraries/_category_.json b/docs/contracts/v4/reference/periphery/libraries/_category_.json deleted file mode 100644 index 57982ec8d..000000000 --- a/docs/contracts/v4/reference/periphery/libraries/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Libraries", - "position": 0, - "collapsed": true -} diff --git a/docs/contracts/v4/reference/periphery/libraries/action-constants.mdx b/docs/contracts/v4/reference/periphery/libraries/action-constants.mdx deleted file mode 100644 index 0d3b06fa5..000000000 --- a/docs/contracts/v4/reference/periphery/libraries/action-constants.mdx +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: ActionConstants.sol ---- - -(TODO: explain gas optimizations of using special constants) \ No newline at end of file diff --git a/docs/contracts/v4/reference/periphery/libraries/actions.mdx b/docs/contracts/v4/reference/periphery/libraries/actions.mdx deleted file mode 100644 index 333fd1118..000000000 --- a/docs/contracts/v4/reference/periphery/libraries/actions.mdx +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: Actions.sol ---- \ No newline at end of file