# LLMs.txt for Uniswap v4 Documentation > Complete reference documentation for Uniswap v4 This file contains all documentation content in a single document following the llmstxt.org standard. ## Using LLMs.txt or LLMs-full.txt ## Understanding Context Windows AI models have a context window, which is the amount of text they can process at once. This is typically the number of tokens that can be processed in a single request (more like it's memory capacity). Once the context window fills up, parts of your conversational history may be lost. It is exactly for this reason, providing relevant context upfront is critical. ## llms.txt and llms-full.txt Depending on the model's context window, you can either use llms.txt or llms-full.txt. Most of the modern models have a [context window of about 100k tokens](https://codingscape.com/blog/llms-with-largest-context-windows). For most cases we are good to go with llms.txt as it is more compact and provides the LLM with necessary information about links from the documentation and what it does. If you are using a model with a larger context window, you can use llms-full.txt. It is a more verbose version of llms.txt and provides the LLM with more information about the documentation. ## Using llms.txt or llms-full.txt file You can provide your code editor with a llms.txt or llms-full.txt file to use Unichain docs as a context for your code. Here's how to do it for some common code editors: #### Cursor 1. Navigate to Cursor Settings > Features > Docs 2. Select “Add new doc” and paste the following URL: ``` https://docs.uniswap.org/v4-llms.txt ``` or ``` https://docs.uniswap.org/v4-llms-full.txt ``` 3. Use @docs -> Uniswap to reference Uniswap docs in your chat. #### Windsurf Windsurf doesn't have a permanent settings menu for adding documentation context, so you need to reference it in each conversation where you want to use it. 1. You can directly add it to the Cascade window (CMD+L) using: ``` @docs:https://docs.uniswap.org/v4-llms.txt ``` or ``` @docs:https://docs.uniswap.org/v4-llms-full.txt ``` --- ## Overview ## Uniswap APIs Welcome to the Uniswap API documentation. Uniswap provides several APIs and data sources to help developers integrate with and build on top of the Uniswap protocol. ## Available APIs ### Trading API The Uniswap Trading API provides quote generation and transaction building for token swaps across 25+ chains. - **[Trading API Overview](./trading/overview)** - Get started with the Trading API - **[Integration Guide](./trading/integration-guide)** - Complete implementation guide with schemas and best practices - **Quote & Swap Endpoints** - Generate quotes and build unsigned transactions - **Permit2 Support** - Gasless approvals via EIP-712 signatures - **Cross-Chain Swaps** - Multi-step cross-chain swap support ### Subgraph API The Uniswap Subgraph provides a GraphQL API for querying historical and real-time data from the Uniswap protocol. - **[Subgraph Documentation](./subgraph/overview)** - Learn how to query Uniswap data using GraphQL - **[Subgraph Examples](./subgraph/guides/v4-subgraph-example)** - Practical examples and queries - **[v3 Subgraph Guide](./subgraph/guides/v3-subgraph-example)** - Legacy v3 protocol queries ### Routing API The Uniswap Routing API provides optimized trade routes and quotes for swaps. - **Smart Order Routing** - Find the best prices across multiple pools - **Gas Optimization** - Routes optimized for gas efficiency - **Multi-hop Support** - Complex routing across token pairs ### Price APIs Get real-time and historical price data for tokens on Uniswap. - **Current Prices** - Real-time token prices - **Historical Data** - Price charts and historical trends - **Pool Information** - Liquidity and volume data ## Getting Started ### For Developers If you're building applications that need to: - Execute token swaps → Use the **[Trading API](./trading/overview)** - Query historical trading data → Use the **Subgraph API** - Get optimal swap routes → Use the **Routing API** - Display token prices → Use the **Price APIs** ### For Data Analysis The Subgraph API is perfect for: - Analytics dashboards - Trading strategy research - Protocol metrics and insights - DeFi research and analysis ## Rate Limits and Usage - **Subgraph API**: Generous rate limits via The Graph - **Routing API**: Production-ready with caching - **Price APIs**: Real-time updates with historical data ## Support and Resources - **Discord**: Join the Uniswap developer community - **GitHub**: Explore code examples and integrations - **Documentation**: Comprehensive guides and references Ready to start building? Choose the API that fits your needs from the navigation menu. --- ## v4 Protocol Query Examples ## Subgraph Query Examples This doc will teach you how to query Uniswap v4 analytics by writing GraphQL queries on the subgraph. You can fetch data points like: - [position details](#general-position-data) - [current liquidity](#pool-data) of a pool - [volume on a certain day](#historical-global-data) and much more. Below are some example queries. To run a query copy and paste it into the [v4 explorer](https://thegraph.com/explorer/subgraphs/DiYPVdygkfjDWhbxGSqAQxwBKmfKnkWQojqeM2rkLb3G?view=Query&chain=arbitrum-one) to get fresh data. ## Global Data Global data refers to data points about the Uniswap v4 protocol as a whole. Some examples of global data points are: - Total value locked in the protocol, - Total pools deployed, - Total transaction counts. Thus, to query global data you must pass in the PoolManager address `0x000000000004444c5dc75cb358380d2e3de08a90` and select the desired fields. Reference the full [poolManager schema](https://github.com/Uniswap/v4-subgraph/blob/main/schema.graphql#L1) to see all possible fields. PoolManager addresses for all supported chains are listed in the [Deployments](/contracts/v4/deployments) section. ### Current Global Data An example querying total pool count, transaction count, and total volume in USD and ETH: ``` { poolManager(id: "0x000000000004444c5dc75cb358380d2e3de08a90") { poolCount txCount totalVolumeUSD totalVolumeETH } } ``` ### Historical Global Data You can also query historical data by specifying a block number. ``` { poolManager( id: "0x000000000004444c5dc75cb358380d2e3de08a90", block: { number: 22451931 } ) { poolCount txCount totalVolumeUSD totalVolumeETH } } ``` ## Pool Data To get data about a certain pool, pass in the pool address. Reference the full [pool schema](https://github.com/Uniswap/v4-subgraph/blob/main/schema.graphql#L76) and adjust the query fields to retrieve the data points you want. ### General Pool Query The query below returns the feeTier, spot price, and liquidity for the ETH-USDC pool. ``` { pool(id: "0x21c67e77068de97969ba93d4aab21826d33ca12bb9f565d8496e8fda8a82ca27") { tick token0 { symbol id decimals } token1 { symbol id decimals } feeTier sqrtPrice liquidity } } ``` ### All Possible Pools The maximum items you can query at once is 1000. Thus to get all possible pools, you can iterate using the skip variable. To get pools beyond the first 1000 you can also set the skip as shown below. ### Skipping First 1000 Pools This query sets the skip value and returns the first 10 responses after the first 1000. ``` { pools(first: 10, skip: 1000) { id token0 { id symbol } token1 { id symbol } } } ``` ### Creating a Skip Variable This next query sets a skip variable. In your language and environment of choice you can then iterate through a loop, query to get 1000 pools each time, and continually adjust skip by 1000 until all pool responses are returned. Check out [this example](https://github.com/Uniswap/v3-info/blob/770a05dc1a191cf229432ebc43c1f2ceb3666e3b/src/data/pools/chartData.ts#L14) from our interface for poolDayData that does something similar. > **Note** > This query will not work in the graph explorer and more resembles the structure of a query you'd pass to some GraphQL middleware like Apollo. ``` query pools($skip: Int!) { pools( first: 1000 skip: $skip orderDirection: asc ) { id sqrtPrice token0 { id } token1 { id } } } ``` ### Most Liquid Pools Retrieve the top 1000 most liquid pools. You can use this similar set up to orderBy other variables like number of swaps or volume. ``` { pools( first: 1000, orderBy: liquidity, orderDirection: desc ) { id } } ``` ### Pool Daily Aggregated This query returns daily aggregated data for the first 10 days since the given timestamp for the UNI-ETH pool. To calculate `poolId`, refer to [PoolId Library](/contracts/v4/reference/core/types/PoolId#toid). ``` { poolDayDatas( first: 10, orderBy: date, where: { pool: "0x21c67e77068de97969ba93d4aab21826d33ca12bb9f565d8496e8fda8a82ca27", date_gt: 1735689600 } ) { date liquidity sqrtPrice token0Price token1Price volumeToken0 volumeToken1 } } ``` ## Swap Data ### General Swap Data To query data about a particular swap, input the transaction hash + "-" + the index in the swaps the transaction array. This is the reference for the full [swap schema](https://github.com/Uniswap/v4-subgraph/blob/main/schema.graphql#L186). This query fetches data about the sender, amounts, transaction data, timestamp, and tokens for a particular swap. ``` { swap(id: "0x0000329e0d864d8e7c93627b76f6b5b99bd776cb18d9f8829e7da469f563e7d4-212") { sender amount0 amount1 transaction { id blockNumber gasUsed gasPrice } timestamp token0 { id symbol } token1 { id symbol } } } ``` ### Recent Swaps Within a Pool You can set the `where` field to filter swap data by pool address. This example fetches data about multiple swaps for the ETH-USDT pool, ordered by timestamp. ``` { swaps( orderBy: timestamp, orderDirection: desc, where: { pool: "0x21c67e77068de97969ba93d4aab21826d33ca12bb9f565d8496e8fda8a82ca27" } ) { pool { token0 { id symbol } token1 { id symbol } } sender amount0 amount1 } } ``` ## Token Data Input the token contract address to fetch token data. Any token that exists in at least one Uniswap v4 pool can be queried. The output will aggregate data across all v4 pools that include the token. ### General Token Data This queries the decimals, symbol, name, pool count, and volume in USD for the UNI token. Reference the full [token schema](https://github.com/Uniswap/v4-subgraph/blob/main/schema.graphql#L37) for all possible fields you can query. ``` { token(id:"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984") { symbol name decimals volumeUSD poolCount } } ``` ### Token Daily Aggregated You can fetch aggregate data about a specific token over a 24-hour period. This query gets 10-days of the 24-hour volume data for the UNI token ordered from oldest to newest. ``` { tokenDayDatas( first: 10, where: { token: "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984" }, orderBy: date, orderDirection: asc ) { date token { id symbol } volumeUSD } } ``` ### All Tokens Similar to retrieving all pools, you can fetch all tokens by using skip. > **Note** > This query will not work in the graph explorer and more resembles the structure of a query you'd pass to some GraphQL middleware like Apollo. ``` query tokens($skip: Int!) { tokens(first: 1000, skip: $skip) { id symbol name } } ``` ## Position Data ### General Position Data To get data about a specific position, input the NFT tokenId. This queries the subscriptions, unsubscriptions, and transfers for the position with tokenId 3. Reference the full [position schema](https://github.com/Uniswap/v4-subgraph/blob/main/schema.graphql#416) to see all fields. ``` { position(id:3) { id subscriptions { id } unsubscriptions { id } transfers { id } } } ``` ## Contribute There are many more queries you can do with the Uniswap v4 subgraph including data related to ticks, subscriptions, unsubscriptions, and more. Once again you can reference the full schema [here](https://github.com/Uniswap/v4-subgraph/blob/main/schema.graphql). If you'd like to suggest more example queries to showcase, feel free to drop some suggestions in [discord](https://discord.com/invite/uniswap) under #dev-chat or [contribute](https://github.com/Uniswap/docs/blob/main/CONTRIBUTING.md) your own queries by submitting a pull request to the docs repo. --- ## Overview(Subgraph) ## The Uniswap Subgraph Uniswap uses multiple [subgraphs](https://thegraph.com/docs/about/introduction#what-the-graph-is) for indexing and organizing data from the Uniswap smart contracts. These subgraphs are hosted on The Graph and can be used to query Uniswap data. ### Versions and Production Endpoints Each version of Uniswap for each chain has its own dedicated subgraph. As hosted subgraphs have been depreciated, you will now need to call through their decentralized subgraphs. Each subgraph has a dedicated endpoint for querying data, as well as a page on [The Graph explorer](https://thegraph.com/explorer) that exposes the schema and available fields to query. ## Creating an API Key API Keys can be created by users inside the [Studio](https://thegraph.com/studio/apikeys/). This key will be included into the endpoint to associate a consumers usage to their billing. ##### v4 (Mainnet) - [Subgraph](https://thegraph.com/explorer/subgraphs/DiYPVdygkfjDWhbxGSqAQxwBKmfKnkWQojqeM2rkLb3G?view=About&chain=arbitrum-one) - Graphql Endpoint: `https://gateway.thegraph.com/api//subgraphs/id/DiYPVdygkfjDWhbxGSqAQxwBKmfKnkWQojqeM2rkLb3G` - Code: https://github.com/Uniswap/v4-subgraph ##### v3 (Mainnet) - [Subgraph](https://thegraph.com/explorer/subgraphs/5zvR82QoaXYFyDEKLZ9t6v9adgnptxYpKpSbxtgVENFV?view=Query&chain=arbitrum-one) - Graphql Endpoint: `https://gateway.thegraph.com/api//subgraphs/id/5zvR82QoaXYFyDEKLZ9t6v9adgnptxYpKpSbxtgVENFV` - Code: https://github.com/Uniswap/v3-subgraph ##### v2 (Mainnet) - [Subgraph](https://thegraph.com/explorer/subgraphs/A3Np3RQbaBA6oKJgiwDJeo5T3zrYfGHPWFYayMwtNDum?view=Query&chain=arbitrum-one) - Graphql Endpoint: `https://gateway.thegraph.com/api//subgraphs/id/A3Np3RQbaBA6oKJgiwDJeo5T3zrYfGHPWFYayMwtNDum` - Code: https://github.com/Uniswap/v2-subgraph ##### v1 (Mainnet) - [Explorer Page](https://thegraph.com/explorer/subgraphs/ESnjgAG9NjfmHypk4Huu4PVvz55fUwpyrRqHF21thoLJ?view=Query&chain=arbitrum-one) - Graphql Endpoint: `https://gateway.thegraph.com/api//subgraphs/id/ESnjgAG9NjfmHypk4Huu4PVvz55fUwpyrRqHF21thoLJ` - Code: https://github.com/graphprotocol/uniswap-subgraph ## v3 Subgraphs for other chains | Chain | V3 | |--|--| | Arbitrum | [Subgraph](https://thegraph.com/explorer/subgraphs/FbCGRftH4a3yZugY7TnbYgPJVEv2LvMT6oF1fxPe9aJM?view=Query&chain=arbitrum-one) | | Base | [Subgraph](https://thegraph.com/explorer/subgraphs/43Hwfi3dJSoGpyas9VwNoDAv55yjgGrPpNSmbQZArzMG?view=Query&chain=arbitrum-one) | | Optimism | [Subgraph](https://thegraph.com/explorer/subgraphs/Cghf4LfVqPiFw6fp6Y5X5Ubc8UpmUhSfJL82zwiBFLaj?view=Query&chain=arbitrum-one) | | Polygon | [Subgraph](https://thegraph.com/explorer/subgraphs/3hCPRGf4z88VC5rsBKU5AA9FBBq5nF3jbKJG7VZCbhjm?view=Query&chain=arbitrum-one) | | BSC | [Subgraph](https://thegraph.com/explorer/subgraphs/F85MNzUGYqgSHSHRGgeVMNsdnW1KtZSVgFULumXRZTw2?view=Query&chain=arbitrum-one) | | Avalanche | [Subgraph](https://thegraph.com/explorer/subgraphs/GVH9h9KZ9CqheUEL93qMbq7QwgoBu32QXQDPR6bev4Eo?view=Query&chain=arbitrum-one) | | Celo | [Subgraph](https://thegraph.com/explorer/subgraphs/ESdrTJ3twMwWVoQ1hUE2u7PugEHX3QkenudD6aXCkDQ4?view=Query&chain=arbitrum-one) | | Blast | [Subgraph](https://thegraph.com/explorer/subgraphs/2LHovKznvo8YmKC9ZprPjsYAZDCc4K5q4AYz8s3cnQn1?view=Query&chain=arbitrum-one) | --- ## Entity Types Following Entity Types are defined in the graph [schema file](https://github.com/Uniswap/v3-subgraph/blob/main/schema.graphql): ||Entity|Description|Schema differs with chain| |-|-|-|-| |1.|[Factory](./schemas/factory.md) | Captures metrics for all the pools deployed by a specific [factory contract](./contracts/factory). |
:white_check_mark:
| |2.|[Bundle](./schemas/bundle) | Stores the current Eth price in USD. || |3.|[Token](./schemas/token) | Stores the metadata and token level metrics for a token present in any of the pools. || |4.|[Pool](./schemas/pool) | Stores a pool's metadata, current & lifetime metrics and links to events and hourly/daily metrics and references to it's tick entities. |
:white_check_mark:
| |5.|[Tick](./schemas/tick) | Stores the metadata for a tick in a pool, it's lifetime metrics and current liquidity and fee variables. |
:white_check_mark:
| |6.|[Position](./schemas/position) | Represents a position created through [NonfungiblePositionManager](../contracts/nonfungiblepositionmanager). Stores it's metadata, deposited/withdrawn tokens, fee variables and transactions where it participated. |
:white_check_mark:
| |7|[PositionSnapshot](./schemas/positionsnapshot) | Saves the state of a position after an action taken on the position. |
:white_check_mark:
| |8|[Transaction](./schemas/transaction) | Stores the list of mint, burn, swap, flash and collects events emitted within a transaction. || |9|[Mint](./schemas/mint) | Stores details of a mint event emitted while adding liquidity to a pool || |10|[Burn](./schemas/burn) | Stores details of a burn event emitted while removing liquidity from a pool || |11|[Swap](./schemas/swap) | Stores details of a swap event emitted while swapping one token for the other in a pool || |12|[Collect](./schemas/collect) | Stores details of a collect event emitted while removing tokens from a position || |13|[Flash](./schemas/flash) | Stores details of a flash event emitted while a flash loan was taken from a pool || |14|[UniswapDayData](./schemas/uniswapdaydata)| Daily stats for all of Uniswap. || |15|[PoolDayData](./schemas/pooldaydata)| Daily stats for each pool |
:white_check_mark:
| |16|[PoolHourData](./schemas/poolhourdata)| Hourly stats for each pool |
:white_check_mark:
| |17|[TickHourData](./schemas/tickhourdata)| Stats on Liquidity available & Volume of token traded at a tick for a given hour || |18|[TickDayData](./schemas/tickdaydata)| Stats on Liquidity available & Volume of token traded at a tick on a given day. (Note: this entity gets saved only if there is a change during the day) |
:white_check_mark:
| |19|[TokenDayData](./schemas/tokendaydata) | Daily stats for a token across all of Uniswap. || |20|[TokenHourData](./schemas/tokenhourdata) | Hourly stats for a token across all of Uniswap. || --- ## Factory Contract ## Contract Details |Chain|Address|StartBlock| |-|-|-| |Mainnet|0x1F98431c8aD98523631AE4a59f267346ea31F984|12369621| |Polygon|0x1F98431c8aD98523631AE4a59f267346ea31F984|22757547| |Arbitrum-One|0x1F98431c8aD98523631AE4a59f267346ea31F984|175| |Optimism|0x1F98431c8aD98523631AE4a59f267346ea31F984|0| ## Events Tracked |Event Name|Event handler| |-|-| |PoolCreated|[handlePoolCreated()](../functions-n-handlers/mappings/factory.ts#handlepoolcreated)| --- ## NonFungiblePositionManager Contract ## Contract Details |Chain|Address|StartBlock| |-|-|-| |Mainnet|0xC36442b4a4522E871399CD717aBDD847Ab11FE88|12369651| |Polygon|0xC36442b4a4522E871399CD717aBDD847Ab11FE88|22760586| |Arbitrum-One|None|None| |Optimism|0xC36442b4a4522E871399CD717aBDD847Ab11FE88|0| ## Events Tracked |Event Name|Event handler| |-|-| |IncreaseLiquidity|[handleIncreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handleincreaseliquidity)| |DecreaseLiquidity|[handleDecreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handledecreaseliquidity)| |Collect|[handleCollect()](../functions-n-handlers/mappings/position-manager.ts#handlecollect)| |Transfer|[handleTransfer()](../functions-n-handlers/mappings/position-manager.ts#handletransfer)| --- ## Pool Contract (Templatized) ## Contract Details Pools are deployed dynamically and the addresses cannot be pre-determined. Thus the template feature of graph protocol is used and the Pool contracts to monitor are added when a new pool is deployed using the factory. The [`handlePoolCreated()`](../functions-n-handlers/mappings/factory.ts#handlepoolcreated) event handler adds a new pool contract to monitor. ## Events Tracked |Event Name|Event handler| |-|-| |Initialize|[handleInitialize()](../functions-n-handlers/mappings/core.ts#handleinitialize)| |Swap|[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)| |Mint|[handleMint()](../functions-n-handlers/mappings/core.ts#handlemint)| |Burn|[handleBurn()](../functions-n-handlers/mappings/core.ts#handleburn)| |Flash|[handleFlash()](../functions-n-handlers/mappings/core.ts#handleflash)| --- ## Events Monitored |Event Name|Contract Type|Event handler|Description| |-|-|-|-| |PoolCreated|[Factory](./contracts/factory)|[handlePoolCreated()](../functions-n-handlers/mappings/factory.ts#handlepoolcreated)|Event emitted when a new pool is deployed using the factory contract| |Initialize|[Pool](./contracts/pool)|[handleInitialize()](../functions-n-handlers/mappings/core.ts#handleinitialize)|Event emitted when a new deployed pool is initialized with current price and is ready for adding liquidity and doing swaps| |Swap|[Pool](./contracts/pool)|[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)|Event emitted when a swap takes place| |Mint|[Pool](./contracts/pool)|[handleMint()](../functions-n-handlers/mappings/core.ts#handlemint)|Event emitted when liquidity is added to the pool| |Burn|[Pool](./contracts/pool)|[handleBurn()](../functions-n-handlers/mappings/core.ts#handleburn)|Event emitted when liquidity is removed from the pool| |Flash|[Pool](./contracts/pool)|[handleFlash()](../functions-n-handlers/mappings/core.ts#handleflash)|Event emitted when a flash loan was taken from the pool| |IncreaseLiquidity|[NonFungiblePositionManager](./contracts/nonfungiblepositionmanager)|[handleIncreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handleincreaseliquidity)|Event emitted when liquidity is added to a new/existing position| |DecreaseLiquidity|[NonFungiblePositionManager](./contracts/nonfungiblepositionmanager)|[handleDecreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handledecreaseliquidity)|Event emitted when liquidity is removed from a position| |Collect|[NonFungiblePositionManager](./contracts/nonfungiblepositionmanager)|[handleCollect()](../functions-n-handlers/mappings/position-manager.ts#handlecollect)|Event emitted when removed liquidity and it's accumulated fee collected by the position owner| |Transfer|[NonFungiblePositionManager](./contracts/nonfungiblepositionmanager)|[handleTransfer()](../functions-n-handlers/mappings/position-manager.ts#handletransfer)|Even emitted whent the NFT representing the position ownership is transferred to a different address| --- ## core.ts path: [`/src/mappings/core.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/mappings/core.ts) ### handleInitialize() ``` Params: - event (initialize): entity of the initialize event emitted in a pool contract ReturnType: void ``` - Handles the initialization of a new pool by setting it's `price` and current `tick` value. - Updates the pools daily and hourly metrics using `updatePoolDayData()` and `updatePoolHourData()`. - Updates Eth's USD price using `getEthPriceInUSD()` . - Updates the token's prices relative to Eth using `findEthPerToken()`. #### Entities 1. [Pool](../../schemas/pool) - Read & Write 2. [Token](../../schemas/token) - Write 3. [Bundle](../../schemas/bundle) - Write #### Dependencies: 1. [updatePoolDayData()](../utils/intervalUpdates.ts#updatepooldaydata) 2. [updatePoolHourData()](../utils/intervalUpdates.ts#updatepoolhourdata) 3. [getEthPriceInUSD()](../utils/pricing.ts#getethpriceinusd) 4. [findEthPerToken()](../utils/pricing.ts#findethpertoken) #### Invoked at: 1. [Initialize Event (Handler)](../../events) - Follows the logic of update, but doesn't save the `pool` entity. - Doesn't save the pool entity - Doesn't update the Eth's USD price, or the token prices relative to ETh. ### handleMint() ``` Params: - event (MintEvent): entity of the initialize event emitted in a pool contract ReturnType: void ``` - updates `txCount`, `totalValueLockedETH` and `totalValueLockedUSD` metrics for `pool`, `factory` and `token` entities. - Increases `pool.liquidity` by `event.params.amount` if the current `pool.tick` value is within the minted tick range. - Creates a new `Mint` entity using `transaction.id` and `pool.txCount` as `mint.id` - Creates tick entities `lowerTick` and `upperTick` if not already present using `createTick()` and updates their `liquidityGross` and `liquidityNet` fields. - Updates the pool and token metrics using `updateUniswapDayData()`, `updatePoolDayData()`, `updatePoolHourData()`, `updateTokenDayData()`, `updateTokenHourData()`. - Updates the fees accumulated outside the lower/upper ticks using `updateTickFeeVarsAndSave()` #### Entities 1. [Bundle](../../schemas/bundle) - Read 2. [Pool](../../schemas/pool) - Read & Write 3. [Token](../../schemas/token) - Read & Write 4. [Factory](../../schemas/factory) - Read & Write 5. [Tick](../../schemas/tick) - Read/Create & Write 6. [Mint](../../schemas/mint) - Create & Write #### Dependencies: 1. [FACTORY_ADDRESS](../utils/constants.ts#factory_address) 2. [convertTokenToDecimal()](../utils/index.ts#converttokentodecimal) 3. [loadTransaction()](../utils/index.ts#loadtransaction) 4. [createTick()](../utils/tick.ts#createtick) 5. [updateUniswapDayData()](../utils/intervalUpdates.ts#updateuniswapdaydata) 6. [updatePoolDayData()](../utils/intervalUpdates.ts#updatepooldaydata) 7. [updatePoolHourData()](../utils/intervalUpdates.ts#updatepoolhourdata) 8. [updateTokenDayData()](../utils/intervalUpdates.ts#updatetokendaydata) 9. [updateTokenHourData()](../utils/intervalUpdates.ts#updatetokenhourdata) 10. [updateTickFeeVarsAndSave()](#updatetickfeevarsandsave) 11. [ONE_BI](../utils/constants.ts#one_bi) #### Invoked at: 1. [Mint Event (Handler)](../../events) ### handleBurn() ``` Params: - event (BurnEvent): entity of the burn event emitted in a pool contract ReturnType: void ``` - updates `txCount`, `totalValueLockedETH` and `totalValueLockedUSD` metrics for `pool`, `factory` and `token` entities. - Decreases `pool.liquidity` by `event.params.amount` if the current `pool.tick` value is within the burnt tick range. - Creates a new `Burn` entity using `transaction.id` and `pool.txCount` as `mint.id`. Sets the values from `event` parameters. - Reduces the liquidity represented by `liquidityGross` and `liquidityNet` fields of the LowerTick and UpperTick. - Updates the pool and token metrics using `updateUniswapDayData()`, `updatePoolDayData()`, `updatePoolHourData()`, `updateTokenDayData()`, `updateTokenHourData()`. - Updates the fees accumulated outside the lower/upper ticks using `updateTickFeeVarsAndSave()` #### Entities 1. [Bundle](../../schemas/bundle) - Read 2. [Pool](../../schemas/pool) - Read & Write 3. [Token](../../schemas/token) - Read & Write 4. [Factory](../../schemas/factory) - Read & Write 5. [Tick](../../schemas/tick) - Read & Write 6. [Burn](../../schemas/burn) - Create & Write #### Dependencies: 1. [FACTORY_ADDRESS](../utils/constants.ts#factory_address) 2. [convertTokenToDecimal()](../utils/index.ts#converttokentodecimal) 3. [loadTransaction()](../utils/index.ts#loadtransaction) 4. [ONE_BI](../utils/constants.ts#one_bi) 5. [updateUniswapDayData()](../utils/intervalUpdates.ts#updateuniswapdaydata) 6. [updatePoolDayData()](../utils/intervalUpdates.ts#updatepooldaydata) 7. [updatePoolHourData()](../utils/intervalUpdates.ts#updatepoolhourdata) 8. [updateTokenDayData()](../utils/intervalUpdates.ts#updatetokendaydata) 9. [updateTokenHourData()](../utils/intervalUpdates.ts#updatetokenhourdata) 10. [updateTickFeeVarsAndSave()](#updatetickfeevarsandsave) #### Invoked at: 1. [Burn Event (Handler)](../../events) Most of the logic is same as mainnet subgraph with following changes: - While loading the `Tick` entities, if either one is not found, invokes `createTickBurn()` to create ticks and then proceeds with updating the liquidity values and metrics. ### Additional Dependencies 1. [createTickBurn()](../utils/tick.ts#createtickburn) ### handleSwap() ``` Params: - event (SwapEvent): entity of the swap event emitted in a pool contract ReturnType: void ``` :::info Ignored Pool The following pool address is ignored by the function: [0x9663f2ca0454accad3e094448ea6f77443880454](https://etherscan.io/address/9663f2ca0454accad3e094448ea6f77443880454) (WETH-LUSD) ::: - Calculates the tracked and untracked USD amount for the swap. `tracked` amount is the USD amount calculated only for tokens present in `WHITELIST_TOKEN` using `getTrackedAmountUSD`. `untracked` amount is calculated using `token.derivedETH * bundle.ethPriceUSD`. - Calculates the fee in `ETH` & `USD` using the formula `amountTracked * (pool.feeTier/1,000,000)`. - Updates the fields for `txCount`, volume & fees (in eth & usd) and `untrackedVolumeUSD` for `pool`, `factory` & `token` entities. - For `pool` entity, sets `liquidity`, `tick`, `sqrtPrice` from the `event` parameters. - Sets the `pool.token0Price` and `pool.token1Price` using `sqrtPriceX96ToTokenPrices()`. - Updates the `bundle.ethPriceUSD` using `getEthPriceInUSD()`. - Updates the `token.derivedETH` value using `findEthPerToken()`. - Updates the `totalValueLockedETH` and `totalValueLockedUSD` for `pool`, `factory` and `token` entities after the USD price update. - Creates a new `Swap` entity using `transaction.id` and `pool.txCount` as `swap.id`. Sets the values from `event` parameters. - Sets `pool.feeGrowthGlobal0X128` and `pool.feeGrowthGlobal1X128` by reading the them from pool contract's blockchain state using the ABI. - Triggers updates to the daily and hourly metrics for pool and tokens. Uses the returned instances to set the fields for volume & fee. - If the updated `pool.tick` is initialized, updates it's fee variables using `loadTickUpdateFeeVarsAndSave()`. - Iterates over all the ticks crossed with the swap (oldTick to newTick) and updates their fee fields using `loadTickUpdateFeeVarsAndSave()`. If the number of ticks cross is more than 100, the updates are ignored to prevent timeouts. #### Entities 1. [Bundle](../../schemas/bundle) - Read & Write 2. [Pool](../../schemas/pool) - Read & Write 3. [Token](../../schemas/token) - Read & Write 4. [Factory](../../schemas/factory) - Read & Write 5. [Tick](../../schemas/tick) - Read/Create & Write 6. [Swap](../../schemas/swap) - Create & Write 7. [UniswapDayData](../../schemas/uniswapdaydata) - Write 8. [PoolDayData](../../schemas/pooldaydata) - Write 9. [PoolHourData](../../schemas/poolhourdata) - Write 10. [TokenDayData](../../schemas/tokendaydata) - Write 11. [TokenHourData](../../schemas/tokenhourdata) - Write #### ABI Dependencies: 1. pool.json #### Dependencies: 1. [FACTORY_ADDRESS](../utils/constants.ts#factory_address) 2. [convertTokenToDecimal()](../utils/index.ts#converttokentodecimal) 3. [loadTransaction()](../utils/index.ts#loadtransaction) 4. [getTrackedAmountUSD()](../utils/pricing.ts#gettrackedamountusd) 5. [safeDiv()](../utils/index.ts#safediv) 6. [sqrtPriceX96ToTokenPrices()](../utils/pricing.ts#sqrtpricex96totokenprices) 7. [getEthPriceInUSD()](../utils/pricing.ts#getethpriceinusd) 8. [findEthPerToken()](../utils/pricing.ts#findethpertoken) 9. [updateUniswapDayData()](../utils/intervalUpdates.ts#updateuniswapdaydata) 10. [updatePoolDayData()](../utils/intervalUpdates.ts#updatepooldaydata) 11. [updatePoolHourData()](../utils/intervalUpdates.ts#updatepoolhourdata) 12. [updateTokenDayData()](../utils/intervalUpdates.ts#updatetokendaydata) 13. [updateTokenHourData()](../utils/intervalUpdates.ts#updatetokenhourdata) 14. [loadTickUpdateFeeVarsAndSave()](#loadtickupdatefeevarsandsave) 15. [feeTierToTickSpacing()](../utils/tick.ts#feetiertotickspacing) 16. [ZERO_BD](../utils/constants.ts#zero_bd) 17. [ZERO_BI](../utils/constants.ts#zero_bi) 18. [ONE_BI](../utils/constants.ts#one_bi) #### Invoked at: 1. [Swap Event (Handler)](../../events) - Follows the logic of mainnet except doesn't save the `token0HourData`, `token1HourData` and `poolHourData` entities. - Follows the logic of mainnet except doesn't save the `token0HourData`, `token1HourData` and `poolHourData` entities. - Doesn't update the `pool.feeGrowthGlobal0X128` and `pool.feeGrowthGlobal1X128` values. ### handleFlash() ``` Params: - event (FlashEvent): entity of the flash event emitted in a pool contract ReturnType: void ``` - Sets `pool.feeGrowthGlobal0X128` and `pool.feeGrowthGlobal1X128` by reading the them from pool contract's blockchain state using the ABI. #### Entities 1. [Pool](../../schemas/pool) - Read & Write #### ABI Dependencies: 1. pool.json #### Invoked at: 1. [Flash Event (Handler)](../../events) - Doesn't update anything. Only loads the pool entity and immediately saves it. ### updateTickFeeVarsAndSave() ``` Params: - tick (Tick): Fee Variables are updated for this tick entity - event (Ethereum.event): An event from the pool the tick represent is in ReturnType: void ``` - Sets `tick.feeGrowthOutside0X128` and `tick.feeGrowthOutside1X128` by reading the tick from pool contract's blockchain state using the ABI. - Triggers update to tick day metrics by invoking `updateTickDayData()`. #### Entities 1. [Tick](../../schemas/tick) - Write #### ABI Dependencies: 1. pool.json #### Dependencies: 1. [updateTickDayData()](../utils/intervalUpdates.ts#updatetickdaydata) #### Invoked at: 1. [handleMint()](#handlemint) 2. [handleBurn()](#handleburn) 3. [loadTickUpdateFeeVarsAndSave](#loadtickupdatefeevarsandsave) - Doesn't update anything. Only loads the ticks from pool contract and invokes save on the tick entity passed as parameter. ### loadTickUpdateFeeVarsAndSave() ``` Params: - tickId (i32): The fee variables are updated for this tickId - event (ethereum.event): An event from the pool contract which the tick is a part of. ReturnType: void ``` - Loads the tick using `event.address` and `tickId`. If found, updates the tick variables by invoking `updateTickFeeVarsAndSave()`. #### Entities 1. [Tick](../../schemas/tick) - Read & Write #### Dependencies: 1. [updateTickFeeVarsAndSave()](#updatetickfeevarsandsave) #### Invoked at: 1. [handleSwap()](#handleswap) --- ## factory.ts path: [`/src/mappings/factory.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/mappings/factory.ts) ### handlePoolCreated() ``` Params: - event (PoolCreated): Event entity representing the Factory Contracts's Pool created event ReturnType: void ``` :::info Ignored Pool The following pool address is ignored by the function: [0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248](https://etherscan.io/address/0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248) (MULAN-USDT) ::: - Loads the factory entity at `FACTORY_ADDRESS`, or creates one if not found. Creates the bundle entity while creating factory. Initializes the new entity to `ZERO_BD`, `ZERO_BI`. - Increment the factory's pool count. - Creates new `Token` entity for each of the tokens in the pool if not already present. Initializes the token parameters using `fetchTokenSymbol()`, `fetchTokenName()`, `fetchTokenTotalSupply()`, `fetchTokenDecimals()` and metrics to `ZERO_BD` or `ZERO_BI`. - If a token is present in the `WHITELIST_TOKENS` list, it is added to the other token's `whitelistPools` list, which is used for calculating the amount in USD. - Creates a new `Pool` entity for the token pair. Uses the event parameters, and `token` entities to initialize the values, while sets the metrics to `ZERO_BI` or `ZERO_BD`. - Finally, using `Pool`(Template), adds a new pool entity to listen for events from the new pool. :::danger Token decimals mandatory While creating either of the tokens, if the decimals value is not available, the pool entity is not created and the function returns without changing any entity. Only the bundle entity is still created if factory entity didn't exist. ::: #### Entities: 1. [Factory](../../schemas/factory) - Read/Create & Write Entity 2. [Bundle](../../schemas/bundle) - Create Entity 3. [Pool](../../schemas/pool) - Create Entity 4. [Token](../../schemas/token) - Read/Create & Write #### Contracts 1. [Pool (Template)](../../contracts/pool) - Create #### Dependencies: 1. [FACTORY_ADDRESS](../utils/constants.ts#factory_address) 2. [ADDRESS_ZERO](../utils/constants.ts#address_zero) 3. [ZERO_BD](../utils/constants.ts#zero_bd) 4. [ZERO_BI](../utils/constants.ts#zero_bi) 5. [ONE_BI](../utils/constants.ts#one_bi) 6. [fetchTokenSymbol()](../utils/token.ts#fetchtokensymbol) 7. [fetchTokenName()](../utils/token.ts#fetchtokenname) 8. [fetchTokenTotalSupply()](../utils/token.ts#fetchtokentotalsupply) 9. [fetchTokenDecimals()](../utils/token.ts#fetchtokendecimals) 10. [WHITELIST_TOKENS](../utils/pricing.ts#whitelist_tokens) #### Invoked at: 1. [PoolCreated Event (Handler)](../../events) - Same logic as mainnet, but doesn't initialize `pool.feeGrowthGlobal0X128` and `pool.feeGrowthGlobal1X128` values. - If factory doesn't exists, initializes poolCount to `104` instead of `ZERO_BI` and `factory.populated` to `false` - Before saving the pool entity, if `factory.populated` is false, invokes `populateEmptyPools()` to load the pool before regenisis and sets `factory.populated = true`. ### Additional Dependencies 1. [populateEmptyPools()](../utils/backfill.ts#populateemptypools) --- ## position-manager.ts :::danger File Missing The complete file is missing for arbitrum-one ::: path: [`/src/mappings/position-manager.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/mappings/position-manager.ts) ### getPosition() ``` Params: - event (ethereum.Event): An event from the NFT Position Manager contract - tokenId (BigInt): NFT Id for the staked position ReturnType: Position | null ``` - Returns a `Position` entity for the given `tokenId` if found. - If not found, retrieves a position by directly querying the `NonfungiblePositionManager` contract using the ABI. Invokes `factoryContract.getPool()` and passing it the `position`'s parameters `token0`, `token1` and `fee` to find the `pool` contract address. - Then creates a new position entity for the tokenId and set the metadata properties using `position` read earlier from the `NonfungiblePositionManager` contract. Sets the metrics to `ZERO_BD`. :::info No Position for same Block Mint and burn In certain scenarios, the position is minted and burnt within the same block. The contract call to NonfungiblePositionManager to retrieve position data reverts in such scenarios as the position no longer exists. ::: #### Entities 1. [Position](../../schemas/position) - Read/Create Without Saving #### ABI Dependencies: 1. NonfungiblePositionManager.json #### Dependencies: 1. [factoryContract](../utils/constants.ts#factorycontract) 2. [ZERO_BI](../utils/constants.ts#zero_bi) 3. [ZERO_BD](../utils/constants.ts#zero_bd) 4. [ADDRESS_ZERO](../utils/constants.ts#address_zero) 5. [loadTransaction()](../utils/index.ts#loadtransaction) #### Invoked at: 1. [handleIncreaseLiquidity()](#handleincreaseliquidity) 2. [handleDecreaseLiquidity()](#handledecreaaseliquidity) 3. [handleCollect()](#handlecollect) 4. [handleTransfer()](#handletransfer) In addition to mainnet: - initializes `position.collectedToken0` and `position.collectedToken1` values. ### updateFeeVars() ``` Params: - position (Position): The position for which the fee variables are set - event (ethereum.Event): An event from the NFT Position Manager contract - tokenId (BigInt): NFT Id for the staked position ReturnType: Position ``` - Updates the fields `position.feeGrowthInside0LastX128` and `position.feeGrowthInside1LastX128` for the position represented by `tokenId` by reading the value from the `NonfungiblePositionManager` triggering the `event`. #### Entities 1. [Position](../../schemas/position) - Update Fields Without Saving #### ABI Dependencies: 1. NonfungiblePositionManager.json #### Invoked at: 1. [handleIncreaseLiquidity()](#handleincreaseliquidity) 2. [handleDecreaseLiquidity()](#handledecreaaseliquidity) 3. [handleCollect()](#handlecollect) ### savePositionSnapshot() ``` Params: - position (Position): Position entity for which the current state is saved as a snapshot - event (ethereum.Event): NonfungiblePositionManager Contract event after which the snapshot is being saved ReturnType: void ``` - Saves the current values of a `Position` entity for future reference, including liquidity, tokens deposited and withdrawn, fee collected, feeGrowthInside. #### Entities 1. [PositionSnapshot()](../../schemas/positionsnapshot) - Create #### Dependencies: 1. [loadTransaction()](../utils/index.ts#loadtransaction) #### Invoked at: 1. [handleIncreaseLiquidity()](#handleincreaseliquidity) 2. [handleDecreaseLiquidity()](#handledecreaaseliquidity) 3. [handleCollect()](#handlecollect) 4. [handleTransfer()](#handletransfer) ### handleIncreaseLiquidity() ``` Params: - event (IncreaseLiquidity): Entity for a IncreaseLiquidity event emitted by NonfungiblePositionManager Contract ReturnType: void ``` :::info Ignored Blocks and Addresses - Block 14317993 is ignored by the function. - Pool address 0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248 (MULAN-USDT) is ignored by the function. ::: - Fetches the position entity using `getPosition()`, passing `event.params.tokenId` and `event` as parameters. - Updates fields `position.liquidity`, `position.depositedToken0` and `position.depositedToken1`. - Triggers `updateFeeVars()` and `savePositionSnapshot()` #### Entities 1. [Position](../../schemas/position) - Write 2. [Token](../../schemas/token) - Read #### Dependencies: 1. [getPosition()](#getposition) 2. [convertTokenToDecimal()](../utils/index.ts#converttokentodecimal) 3. [updateFeeVars()](#updatefeevars) 4. [savePositionSnapshot()](#savepositionsnapshot) #### Invoked at: 1. [IncreaseLiquidity Event (Handler)](../../events) Follows most of the logic of mainnet except the following points: - No blocks ingored like mainnet. - Updates field `position.amountDepositedUSD` by deriving `amount0` and `amount1` in their respective USD priced using `bundle.ethPriceUSD` and `token.derivedETH`. #### Additional Entities Referenced 1. [Bundle](../../schemas/bundle) - Read Follows most of the logic of mainnet except the following points: - No blocks ingored like mainnet. - Returns without any changes if token0 or token1 entities are null. #### Additional Entities Referenced 1. [Bundle](../../schemas/bundle) - Read ### handleDecreaseLiquidity() ``` Params: - event (DecreaseLiquidity): Entity for a DecreaseLiquidity event emitted by NonfungiblePositionManager Contract ReturnType: void ``` :::info Ignored Blocks and Addresses - Block 14317993 is ignored by the function. - Pool address 0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248 (MULAN-USDT) is ignored by the function. ::: - Fetches the position entity using `getPosition()`, passing `event.params.tokenId` and `event` as parameters. - Updates fields `position.liquidity`, `position.withdrawnToken0` and `position.withdrawnToken1`. - Triggers `updateFeeVars()` and `savePositionSnapshot()` #### Entities 1. [Position](../../schemas/position) - Write 2. [Token](../../schemas/token) - Read #### Dependencies: 1. [getPosition()](#getposition) 2. [convertTokenToDecimal()](../utils/index.ts#converttokentodecimal) 3. [updateFeeVars()](#updatefeevars) 4. [savePositionSnapshot()](#savepositionsnapshot) #### Invoked at: 1. [DecreaseLiquidity Event (Handler)](../../events) Follows most of the logic of mainnet except the following points: - No blocks ingored like mainnet - Updates field `position.netWithdrawnUSD` by deriving `amount0` and `amount1` in their respective USD priced using `bundle.ethPriceUSD` and `token.derivedETH`. #### Additional Entities 1. [Bundle](../../schemas/bundle) - Read Follows most of the logic of mainnet except the following points: - No blocks ingored like mainnet. - Returns without any changes if token0 or token1 entities are null. #### Additional Entities Referenced 1. [Bundle](../../schemas/bundle) - Read ### handleCollect() ``` Params: - event (Collect): Entity for a Collect event emitted by NonfungiblePositionManager Contract ReturnType: void ``` :::info Ignored Addresses - Pool address 0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248 (MULAN-USDT) is ignored by the function. ::: - Fetches the position entity using `getPosition()`, passing `event.params.tokenId` and `event` as parameters. - Updates fields `position.collectedFeesToken0` and `position.collectedFeesToken1` by adding the `event.params.amount0` after adjusting it with `token.decimals`. - Triggers `updateFeeVars()` and `savePositionSnapshot()` :::danger Incorrect Collected Fees Token1 amount `event.params.amount0` (adjusted with `token0.decimals`) is added to both `position.collectedFeesToken0` and `position.collectedFeesToken1`. This logic needs to be validated. ::: #### Entities 1. [Position](../../schemas/position) - Write 2. [Token](../../schemas/token) - Read #### Dependencies: 1. [getPosition()](#getposition) 2. [convertTokenToDecimal()](../utils/index.ts#converttokentodecimal) 3. [updateFeeVars()](#updatefeevars) 4. [savePositionSnapshot()](#savepositionsnapshot) #### Invoked at: 1. [Collect Event (Handler)](../../events) Differs from mainnet at the following areas: - Updates fields `position.collectedToken0` and `position.collectedToken1` by adding the `event.params.amount0` and `event.params.amount1` after adjusting them with `token.decimals`. - Updates fields `position.collectedFeesToken0` and `position.collectedFeesToken1` by subtracting `position.withdrawnToken` from `position.collectedToken`. - Updates field `position.amountCollectedUSD` by deriving `amount0` and `amount1` in their respective USD priced using `bundle.ethPriceUSD` and `token.derivedETH` and adding to the existing value. #### Additional Entities 1. [Bundle](../../schemas/bundle) - Read Differs from mainnet at the following areas: - Returns without any changes if token0 or token1 entities are null. - Updates fields `position.collectedToken0` and `position.collectedToken1` by adding the `event.params.amount0` and `event.params.amount1` after adjusting them with `token.decimals`. - Updates fields `position.collectedFeesToken0` and `position.collectedFeesToken1` by subtracting `position.withdrawnToken` from `position.collectedToken`. #### Additional Entities Referenced 1. [Bundle](../../schemas/bundle) - Read ### handleTransfer() ``` Params: - event (Transfer): Entity for a Transfer event emitted by NonfungiblePositionManager Contract ReturnType: void ``` - Fetches the position entity using `getPosition()`, passing `event.params.tokenId` and `event` as parameters. - Sets `position.owner` with `event.params.to`. - Triggers `savePositionSnapshot()`. #### Entities 1. [Position](../../schemas/position) - Write #### Dependencies: 1. [getPosition()](#getposition) 2. [savePositionSnapshot()](#savepositionsnapshot) #### Invoked at: 1. [Transfer Event (Handler)](../../events) --- ## Pool Mapping :::info Only in Optimism The file is present in Optimism only ::: ### POOL_MAPPINGS The table captures pool addresses pre-regenesis provided by the Optimism team. This is needed as subgraph indexer has no knowledge for pre-regenesis events. |Old Address|New Address|Token0 Address | Token1 Address | |-|-|-|-| |0x8c505fd76eed0945699265c7c7e5bbf756b7e5ad|0x03af20bdaaffb4cc0a521796a223f7d85e2aac31|0x4200000000000000000000000000000000000006|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x8c505fd76eed0945699265c7c7e5bbf756b7e5ad|0x827f0a2a4376bc26729f398b865f424dc8456841|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0xdd54251a35078ba39e3ad5fb059f9aa243693b9d|0x73b14a78a0d396c521f954532d43fd5ffe385216|0x4200000000000000000000000000000000000006|0x68f180fcCe6836688e9084f035309E29Bf0A2095| |0x0ad1af4178e17d7f41dbcdf9d573701bef5eb501|0xdd0c6bae8ad5998c358b823df15a2a4181da1b80|0x4200000000000000000000000000000000000006|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0xdf42e37f057c61765fe7204642c4d2e5ff929cfe|0x815ae7bf44dda74ed9274377ed711efc8b567911|0x4200000000000000000000000000000000000006|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x6b952bfbfda057a7f288edaa9f611cd446ddbe22|0x95d9d28606ee55de7667f0f176ebfc3215cfd9c0|0x4200000000000000000000000000000000000006|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x0bec645f0373750fe0256ee0e7b06d63eae5e04d|0x2df05e4cdbd758cb1a99a34bb0d767e040d6b078|0x4200000000000000000000000000000000000006|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x6afd8618459729da24ee36978567fb04fe5fd1bd|0x85c31ffa3706d1cce9d525a00f1c7d4a2911754c|0x4200000000000000000000000000000000000006|0x68f180fcCe6836688e9084f035309E29Bf0A2095| |0xa61dea82c7c3e64a6a80550aacb251eed604b46b|0x37ffd11972128fd624337ebceb167c8c0a5115ff|0x4200000000000000000000000000000000000006|0x68f180fcCe6836688e9084f035309E29Bf0A2095| |0xcf438c19332d507326210da527fb9cf792fd3e18|0xc858a329bf053be78d6239c4a4343b8fbd21472b|0x4200000000000000000000000000000000000006|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0xaddd011cb3b61d0dc4f85c2661cc9bd1bd640067|0xb29a022ff4b37bdfb21e5f1daff4af5a22aa9510|0x4200000000000000000000000000000000000006|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4| |0x47516ccba929c607e14dbd02f2ebac1e7960b1f8|0x0392b358ce4547601befa962680bede836606ae2|0x4200000000000000000000000000000000000006|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4| |0x13b2d83ec506b5c770f64ee0f564ff9719c74071|0xfea834a5c47b923add607cc5b96288d18ffb9c3f|0x4200000000000000000000000000000000000006|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4| |0xd20bf925e04933ff79274479009218dedab6657f|0xac721d2e27ca148f505b5106fc95e594c78ace5b|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x7678f1e1ed90efec8757af161ab25bf1e8e00238|0xa13514b5444e50067f6e48c386016b211773cf9e|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x2816913eda0010af856d323724f521fb702a25a7|0xcf2aebb91fec906f51fc11cd57035a09d8b16965|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x2419a5fee0f8e0192869507ed6a301382ad9edda|0xea0f33940eb221aaad9360891cab08ef4f1f0703|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x0ccf6bf2df83d250d0f6a636215ef7d19f86dd01|0x703eb589321f3dc7408e9dde01b790e64a9fe4e9|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x1135e9ce18373238c77ff602a9b0a579ca86eb8e|0xc22662b904d98e45f89e030201355c3e372cc819|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x2f5ccaf670e9c5f4336c127a29fdd4932f238069|0x1aa9b4d9933ff96b2011fddd764240d4a16b7c07|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x0f641370eb5cb4f0b0d58140d5fb2f97ffcbfce5|0x2459023a29d3b07711b8b916d86aa7e8a14747af|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0xa14e5b3ba5dd981b536e0950390b03972b795018|0xadb35413ec50e0afe41039eac8b930d313e94fa4|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0xbdb9a8279a525bafe9be7efb9b5df79b18eeb23f|0x84eb2c5c23999b3ddc87be10f15ccec5d22c7d97|0x4200000000000000000000000000000000000006|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0xa194977b416f082f71a0362041b57208c91ee1c1|0x2e80d5a7b3c613d854ee43243ff09808108561eb|0x4200000000000000000000000000000000000006|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0xc3099d7fd3fc7d4feea11911fbe6eadc94c7c07a|0x3d44cc727fe2f603e4929be164c70edb3b498b5f|0x4200000000000000000000000000000000000006|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0xfe901e734c8c55645731acd4eb0be963d2a85b94|0xa588c9d2884c60b098c5ad028ec2f4a1fab772b5|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4| |0xeaa5ba3ef450887e4d5a627700aef3c1a16d4090|0xc53f2be3331926d2f30ee2b10362bb45fdbe7bf6|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0x263312f667279452ad44cda7971fe93f18b6dad4|0x0843e0f56b9e7fdc4fb95fabba22a01ef4088f41|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x051580636f94b8b6ba69b879958939d324d8f650|0x8184f5cf4921558c201923ef6d7d5258a6efa31f|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0xbb8a699cbd6b45f7c31dcd14bd6d965ab4293e2c|0x8b057f0ccd9fb78f688472574cf3f9d2322f5454|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x98fd8560e184136f482054c19a63e644240e30f4|0x9f08065dfc4817a0a56db7bcab757e86399bc51d|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0xbeafe824395fff8df37c4814e8de9d455e79cdad|0x7628784d2c5d47fcd5479ba812343b1aabad6484|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0xcfe7288e10994555ca97dfa2d0c50e55a4d4dc39|0xceb488e01c8e2e669c40b330bfc1440921c9ebe2|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0x4cff717ff0b0a4a3578e8bbb7a5f06d32574238b|0x25e412992634b93a025e2a538c53222a8c62e2d6|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0x072611197970d6a9e57680f97f177ff947f09139|0xc0f184c6c4832b3ed861bd5b05722792ffa64abd|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0x380ff418bf1589b46e9660c6b2197b4ce8ae8a12|0xf046d8b7365d8abe5a8f8301c669b4b5284fc21d|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0xac8c823548f13874dcfc76029089de01f4adc1d3|0x1f2390484dfe2d8900bc91c7111d274b7b2d63a1|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x8cf0a5fdcaed0956a3221e1dd5219bb14f092595|0xa0959d2dcd9dd56bf080a10cfe29eeb401344e3d|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0xaf00e47d2fe45befc4540fe02a87cb053e252065|0x30be2fff09fcd820a1d472e646bd233dbd812133|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x00a0e3cd857a7e5676c901bd349ed1d6afb59fb3|0x3202c46666e774b44ba463eafaa6da9a968a058f|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0x4200000000000000000000000000000000000006| |0x90fc3f5f84fb868b7693b1f2690b91f28c1600d0|0x85e8d0fddf559a57aac6404e7695142cd53eb808|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0x68f180fcCe6836688e9084f035309E29Bf0A2095| |0xc87adb8ac31434e96b429ced522ed84a2ce707a6|0x22fc5dc36811d15fafde7cc7900ae73a538e59e0|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x466fd9d58bdd0e246cbe9112d95d077b81b341af|0xe7ee03b72a89f87d161425e42548bd5492d06679|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x7b6467b86878b86163bcb3162d84e34ea5c7389b|0xfe1bd31a79163d6277ab8c2917d7857c225db065|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0xc24383ba6d156706864a48f50fc01e89c0bf11d7|0xbf595eb9a512b1c274125264aef84a2847158eb3|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4| |0x93d9dfb5caf591df911b251db4d76cd95f4644b7|0x124657e5bb6afc12a15c439d08fc80070f9a1a1e|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x36c95ae265883c2b19e61997760b110cc05e4a60|0xd6101cda1a51924e249132cbcae82bfcd0a91fbc|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0xd515990647c39c4a0b8c03f811f9b746958a0eec|0x19ea026886cbb7a900ecb2458636d72b5cae223b|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0x4200000000000000000000000000000000000006| |0x3b6479c7748eb5b143a3a52d237c0097734b811b|0x5aacc66073cb0c3064353f1441c2e04170b4dbbf|0x4200000000000000000000000000000000000006|0xc5Db22719A06418028A40A9B5E9A7c02959D0d08| |0x2d073707207098cc69e8e86c6a3fd12644b8a1b2|0x4284b21e76d1b3977cab8f0032867e00e6eea382|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0xc5Db22719A06418028A40A9B5E9A7c02959D0d08| |0x015986c7074ec1eeae0387a8baf485fd9d811b7d|0x2f10a1a3e640ad1615cbedf95a1749a4af88cbc0|0xB548f63D4405466B36C0c0aC3318a22fDcec711a|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x4a88e6fa2afad460befd586fc1581f322308c490|0x32846ede08688d10a9da59387707a8fbb0790fa7|0x4200000000000000000000000000000000000006|0xab7bAdEF82E9Fe11f6f33f87BC9bC2AA27F2fCB5| |0xccf24898ca659afa8cb6a3bdb8a2e0a2debda12d|0xbdb6371fffc1753b33b87c68c827eb7978670515|0x4200000000000000000000000000000000000006|0x6fd9d7AD17242c41f7131d257212c54A0e816691| |0x856d50c587824f84de481ea706208b03db38f6f2|0x2eee8ed7df992f23d7554b0db8835d483cce901c|0x298B9B95708152ff6968aafd889c6586e9169f1D|0x68f180fcCe6836688e9084f035309E29Bf0A2095| |0xe3d8cfc3a0b43d2288b3da41563b1fe0623209de|0x65dc095b35679005229896566928f6852948092b|0x298B9B95708152ff6968aafd889c6586e9169f1D|0x68f180fcCe6836688e9084f035309E29Bf0A2095| |0x62196490fcf045437e5e4cb49228bbd778b7196d|0x2d6497dd08a1620d386ce708edac50aaec332415|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9|0xe405de8f52ba7559f9df3c368500b6e6ae6cee49| |0x3b1f6287be238c9b0a4b48d85d2359d58aaa9683|0x039ae8860fbfdf61f654b1a5b55cc3aa753f5842|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x91e50b184ea237b3da1c005ee5d2a17a904a34c6|0x24342b5d46f69ba05c09becdd00e5324f9f0f7ca|0x298B9B95708152ff6968aafd889c6586e9169f1D|0x4200000000000000000000000000000000000006| |0x518767d8ef1acffd978581c16789f8a2803f9bef|0xf0d0e52da1fdde512af299f3d8ea1c5e3bebb96f|0x4200000000000000000000000000000000000006|0xe3C332a5DcE0e1d9bC2cC72A68437790570C28a4| |0xc2c0786e85ac9b0b223966d040ebc641fa44225e|0xb589969d38ce76d3d7aa319de7133bc9755fd840|0x4200000000000000000000000000000000000006|0x7F5c764cBc14f9669B88837ca1490cCa17c31607| |0xa2389a4ee391b4b04ae8dc664664190f3d28f2fe|0x8eda97883a1bc02cf68c6b9fb996e06ed8fdb3e5|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0xf9ca53854d1ac7adb43d9447aa87f17fe1454e31|0x100bdc1431a9b09c61c0efc5776814285f8fb248|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x4893c5f29301cfd7a6527331f3e06ea82e68a952|0xe229ce1cdbea9983362ca29f0f0b2c70bb2dacdf|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0xf2e805fe3b15297e1df03b6036d01b32ab8f7998|0xd9b160620447d9a9a6ca90c0450f5490e5219257|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x6663eea65669978481bae55814cbc496acd50352|0x1179b19438a622fe36be5f9c073b700420384397|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x7bbc5726e6c2640ed0f0fda1546dc232dc5db89c|0xf3f3433c3a97f70349c138ada81da4d3554982db|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x0d4294ae819ff83a4e2a99db8d06cdd025c19218|0x85149247691df622eaf1a8bd0cafd40bc45154a9|0x4200000000000000000000000000000000000006|0x7F5c764cBc14f9669B88837ca1490cCa17c31607| |0x9e845c705aba9a2ca6e97c2423797e18a98d34c0|0x6fa1ea0ccbbe9b2ad52440c88a47b5d73cd9a731|0x298B9B95708152ff6968aafd889c6586e9169f1D|0x4200000000000000000000000000000000000006| |0x00a4dfb447a43a583d8e07eae9d4efbb3656cbcb|0xad4c666fc170b468b19988959eb931a3676f0e9f|0x4200000000000000000000000000000000000006|0x6fd9d7AD17242c41f7131d257212c54A0e816691| |0xdeb1106b510d94df3bcc55e74f51a6f6b231d97e|0x4983691a26d55eb9e18d2e12e3b770cdd3f76a5f|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0xd2243a43813cd7c4bfb2287f32d3989b0f2f67d5|0x8e2eaef2c05ef93f424a8324b94e725eaa362f91|0x4200000000000000000000000000000000000006|0x6fd9d7AD17242c41f7131d257212c54A0e816691| |0x0b3a6896345b68539571aab140134630151ebc68|0x8531e48a8611729185be9eaad945acbd6b32e256|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0x4200000000000000000000000000000000000006| |0x99959743247f2fa2e97b33e532337eae616beeda|0xeb1817b708415f4f78c5f0c99cbbd6a3a899fa6d|0x6fd9d7AD17242c41f7131d257212c54A0e816691|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x94ee41af02171d6dc1e05b790de547fa50dbd7cf|0x26e7fed14a97e0c482a302237971cf1b04f6d3e9|0x6fd9d7AD17242c41f7131d257212c54A0e816691|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x7613311fbb6a4580cdd602f9978c317b2a783d5f|0x3926a81afe5c9c3d05296e4fac4728ba5411ac78|0x6fd9d7AD17242c41f7131d257212c54A0e816691|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x89fe55759966831d747669bfbda477ebf09475d6|0x7a5ea63fe3430a3b9a06fd80a4a9afaa17c1e878|0x4200000000000000000000000000000000000006|0xB27E3Eab7526bF721ea8029bFcd3fDc94c4f8b5b| |0x1c536614fd8ed5faba94528782fbc886c426651a|0xf5a389030a565c13d6e6bbe9342ac9d31dc7521a|0x4200000000000000000000000000000000000006|0xe405de8f52ba7559f9df3c368500b6e6ae6cee49| |0xb0e9a44258cce8ef36c87e8f252aa6bf7cd4b245|0x6168ec836d0b1f0c37381ec7ed1891a412872121|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x7F5c764cBc14f9669B88837ca1490cCa17c31607| |0xcd7b42cee81a3394ee58dab93bbfc87cab03adb5|0x2024c394741a5301e89a375b7bf52f865bc166fd|0x6fd9d7AD17242c41f7131d257212c54A0e816691|0x7F5c764cBc14f9669B88837ca1490cCa17c31607| |0x88f8cd42570f74ff3ef5acd090419070c6efe37a|0x91cca461ee9435848ac0da8fc416ad0816272786|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0xd4bbfe5b58381ba4b9ce87146ce9e5a2d1057d3e|0x865d39d66dee5719e6bee98885ef40b9a36bf56e|0x6fd9d7AD17242c41f7131d257212c54A0e816691|0x94b008aA00579c1307B0EF2c499aD98a8ce58e58| |0x9b8ad5085af53eff13f3ddadcafa453549f7a93f|0x1fff624960ff9d0556420f3647d6aaf06389aab1|0x6fd9d7AD17242c41f7131d257212c54A0e816691|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0x188530f0c09e56e6e30dd5ef76a9b3f0dc403763|0xc8c07386e29f3f239b91019d5426ae139c5bd17b|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0x96db852D93c2feA0F447D6Ec22E146e4e09Caee6| |0x704baee64df71741cf3029652dc99101adc846f0|0x1b19825a9e32b1039080acb1e1f9271314938b96|0x7FB688CCf682d58f86D7e38e03f9D22e7705448B|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0xe4decbe898a6b8bb79ac48e93681cd04d7b1ca1b|0x602a4d0f9e8d40ad3f620050efd1690da908dc0d|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0x7FB688CCf682d58f86D7e38e03f9D22e7705448B| |0x251144c131413a5f6e54001cb586f9101b447059|0x345ddb5743859efce0e6e8293ebd35373d34b6c7|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0xe405de8f52ba7559f9df3c368500b6e6ae6cee49| |0x4065c249115481baaec5c6a16929592935d29ec1|0x94ad9a19126ebb02dda874237e5820fd4943f5de|0x4200000000000000000000000000000000000006|0x7F5c764cBc14f9669B88837ca1490cCa17c31607| |0x6c66eb2798bf42455b63cff3fa3e5bcc3d31848f|0x905707e5c7a10e8351bbd03347be8b5f5de7301a|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x6fd9d7AD17242c41f7131d257212c54A0e816691| |0xa0eed53ea02a174e4ee81d88d3970b5198580b52|0x7d1602f342787f80aef458c10e741149a1697447|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x6fd9d7AD17242c41f7131d257212c54A0e816691| |0xb61a5a79a83ff386dbe40a1bc95578856ab2fa5f|0xa7bb0d95c6ba0ed0aca70c503b34bc7108589a47|0x68f180fcCe6836688e9084f035309E29Bf0A2095|0x7F5c764cBc14f9669B88837ca1490cCa17c31607| |0xc102e1de27d8467589cc65f4b4b18d534f6fdac6|0xb0eca217602b031e03956553fb510085c9f2df28|0x4200000000000000000000000000000000000006|0x8F69Ee043d52161Fd29137AeDf63f5e70cd504D5| |0x61057f7f7c2e338c36fd29433d7977b618348cd0|0x320616dbe138aa2f3db7a5a46ba79a13032cc5f2|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4| |0x05c1d7c9b9b4f1c38859681bd7b4eebb4c373a8e|0xba213008fe93b3591e439f3b2aa51b3e4a2bd7c7|0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6|0x7F5c764cBc14f9669B88837ca1490cCa17c31607| |0xbf592a3a4c64c8c28b667d060336e25480fe6c48|0x680b4eb8b9b8533d503a545adad4af9f00df5f05|0x4200000000000000000000000000000000000006|0x7C17611Ed67D562D1F00ce82eebD39Cb7B595472| |0xb2ab739b499ff9fa019ff944135b4974942b3a95|0x296b88b607ea3a03c821ca4dc34dd9e7e4efa041|0x4200000000000000000000000000000000000006|0x7FB688CCf682d58f86D7e38e03f9D22e7705448B| |0x3e9ef76529932226742113984e6a6c7cea7e2452|0xa99638e4ac81d4ce32c945c1415f89ab8d86bf2c|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9| |0xcb590932a77e02aac00c83bbba4d8014efbebb89|0x9bb3267c4c3e69a961479c475f8fcc4c300af5bd|0x4200000000000000000000000000000000000006|0x7FB688CCf682d58f86D7e38e03f9D22e7705448B| |0x3643c5840fc0ccf4f667a35a151e10302d4d0d23|0x65f8a80d8049a77619435f841055fa4c8d785c47|0x4200000000000000000000000000000000000006|0x96db852D93c2feA0F447D6Ec22E146e4e09Caee6| |0x805b9cf595282d807adfe84a89bec85be5d07f53|0xd3265ea86af798659b4132a453e7cdb29b877e10|0x96db852D93c2feA0F447D6Ec22E146e4e09Caee6|0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1| |0x1a718270a5b014209fb77ac2985556ee471b29af|0x8dfc59e8b119bffa5f552642028e005b1972edc4|0x6fd9d7AD17242c41f7131d257212c54A0e816691|0x96db852D93c2feA0F447D6Ec22E146e4e09Caee6| |0xb91cf01b64c6e6540c45ae356554599cbe92831f|0xc210aeb4e84e0c3b6ee5816858984d52d04f0219|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0x96db852D93c2feA0F447D6Ec22E146e4e09Caee6| |0x8956827b23063c82d0c697004f0015b454a2f107|0x9aaa481a863e95168c01f23640b357b014dff09a|0x7F5c764cBc14f9669B88837ca1490cCa17c31607|0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4| |0x86cf7e458ce79afe44924263d58ef1fd57d1b57c|0x25cc77a38f8de3b9b090fea8f0f5995c4e10a386|0x4200000000000000000000000000000000000006|0xe0BB0D3DE8c10976511e5030cA403dBf4c25165B| #### References At: 1. [populateEmptyPools()](./utils/backfill.ts#populateemptypools) --- ## backfill.ts :::info Only in Optimism The file is present only for the optimism subgraph ::: path: [`/src/utils/backfill.ts`](https://github.com/Uniswap/v3-subgraph/blob/ian/optimism-fix/src/utils/backfill.ts) ### populateToken() ``` Params: - tokenAddress (String): ReturnType: void ``` - Initializes a new Token Enttity for the token address if not already present. - Initializes the token parameters using `fetchTokenSymbol()`, `fetchTokenName()`, `fetchTokenTotalSupply()`, `fetchTokenDecimals()` and metrics to `ZERO_BD` or `ZERO_BI`. #### Entities: 1. [Token](../../schemas/token) - Read/Create & Write #### Dependencies: 1. [fetchTokenSymbol()](./token.ts#fetchtokensymbol) 2. [fetchTokenName()](./token.ts#fetchtokenname) 3. [fetchTokenTotalSupply()](./token.ts#fetchtokentotalsupply) 4. [fetchTokenDecimals()](./token.ts#fetchtokendecimals) 5. [ZERO_BD](../utils/constants.ts#zero_bd) 6. [ZERO_BI](../utils/constants.ts#zero_bi) #### Invoked at: 1. [populateEmptyPools()](#populateemptypools) ### populateEmptyPools() ``` Params: - event (ethereum.Event): ReturnType: void ``` Create entities for each pool and token before regenesis of optimism chain. - Iterates through the pools present in `POOL_MAPPINGS` list. Fow each of the items, does the below mentioned steps. - Using PoolABI, loads the pool contract. Creates a new `Pool` entity and sets it's `token0`, `token1` values from POOL_MAPPING and liquidity from the contract read. Iniitalizes all the metrics to `ZERO_BD` or `ZERO_BI`. - set `pool.feeTier` by reading it from the contract. - Invokes `populateToken()` for the two tokens. - For each token, adds pool to `token.whitelistPool` if the other token is present in `WHITELIST_TOKENS` list. - Sets `pool.totalValueLockedToken` and `token.totalValueLocked` by reading the ERC20 contract and invoking `balanceOf()`. - Saves the token and pool entities. :::danger Overwrites Token TVL `token.tokenValueLocked` is set everytime instead of adding it to the existing balance. Thus if a token is present in multiple pools, the TVL in the from the last pool will be shown. ::: #### Entities: 1. [Pool](../../schemas/pool) - Create & Write 2. [Token](../../schemas/token) - Read/Create & Write #### Contracts 1. [Pool (Template)](../../contracts/pool) - Create #### ABI Dependencies: 1. pool.json 2. ERC20.json #### Dependencies: 1. [POOL_MAPPINGS](../poolMapping.ts#pool_mappings) 2. [ZERO_BD](./constants.ts#zero_bd) 3. [ZERO_BI](./constants.ts#zero_bi) 4. [populateToken()](#populatetoken) 5. [WHITELIST_TOKENS](./pricing.ts#whitelist_tokens) 6. [convertTokenToDecimal()](./index.ts#converttokentodecimal) #### Invoked at: 1. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) --- ## constants.ts path: [`/src/utils/constants.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/utils/constants.ts) ### ADDRESS_ZERO ``` - type: String - value: '0x0000000000000000000000000000000000000000' ``` Represents the null address in ethereum. #### Referenced at: 1. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) 2. [getPosition()](../mappings/position-manager.ts#getposition) ### FACTORY_ADDRESS ``` - type: String - value: '0x1F98431c8aD98523631AE4a59f267346ea31F984' ``` #### Referenced at: 1. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) 2. [factoryContract](#factorycontract) 3. [handleMint()](../mappings/core.ts#handlemint) 4. [handleBurn()](../mappings/core.ts#handleburn) 5. [handleSwap()](../mappings/core.ts#handleswap) 6. [updateUniswapDayData()](./intervalUpdates.ts#updateuniswapdaydata) ### ZERO_BI ``` - type: BigInt - value: 0 ``` #### Referenced at: 1. [updatePoolDayData()](./intervalUpdates.ts#updatepooldaydata) 2. [updatePoolHourData()](./intervalUpdates.ts#updatepoolhourdata) 3. [findEthPerToken()](./pricing.ts#findethpertoken) 4. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) 5. [exponentToBigDecimal()](./index.ts#exponenttobigdecimal) 6. [bigDecimalExponated()](./index.ts#bigdecimalexponated) 7. [tokenAmountToDecimal()](./index.ts#tokenamounttodecimal) 8. [priceToDecimal()](./index.ts#pricetodecimal) 9. [convertTokenToDecimal()](./index.ts#converttokentodecimal) 10. [createTick()](./tick.ts#createtick) 11. [getPosition()](../mappings/position-manager.ts#getposition) 12. [handleSwap()](../mappings/core.ts#handleswap) #### Additionally Invoked At: 1. [populateToken()](./backfill.ts#populatetoken) 2. [populateEmptyPools()](./backfill.ts#populateemptypools) ### ONE_BI ``` - type: BigInt - value: 1 ``` #### Referenced at: 1. [updatePoolDayData()](./intervalUpdates.ts#updatepooldaydata) 2. [updatePoolHourData()](./intervalUpdates.ts#updatepoolhourdata) 3. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) 4. [exponentToBigDecimal()](./index.ts#exponenttobigdecimal) 5. [bigDecimalExponated()](./index.ts#bigdecimalexponated) 6. [handleMint()](../mappings/core.ts#handlemint) 7. [handleBurn()](../mappings/core.ts#handleburn) 8. [handleSwap()](../mappings/core.ts#handleswap) ### TWO_BI ``` - type: BigInt - value: 2 ``` #### Referenced at: 1. [bigDecimalExponated()](./index.ts#bigdecimalexponated) The Value is not present in any other chain. ### ZERO_BD ``` - type: BigDecimal - value: 0 ``` #### Referenced at: 1. [updatePoolDayData()](./intervalUpdates.ts#updatepooldaydata) 2. [updatePoolHourData()](./intervalUpdates.ts#updatepoolhourdata) 3. [updateTokenDayData()](./intervalUpdates.ts#updatetokendaydata) 4. [updateTokenHourData()](./intervalUpdates.ts#updatetokenhourdata) 5. [updateUniswapDayData()](./intervalUpdates.ts#updateuniswapdaydata) 6. [findEthPerToken()](./pricing.ts#findethpertoken) 7. [getTrackedAmountUSD()](./pricing.ts#gettrackedamountusd) 8. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) 9. [safeDiv()](./index.ts#safediv) 10. [equalToZero()](./index.ts#equaltozero) 11. [createTick()](./tick.ts#createtick) 12. [getPosition()](../mappings/position-manager.ts#getposition) 13. [handleSwap()](../mappings/core.ts#handleswap) #### Additionally Invoked At: 1. [populateToken()](./backfill.ts#populatetoken) 2. [populateEmptyPools()](./backfill.ts#populateemptypools) ### ONE_BD ``` - type: BigDecimal - value: 1 ``` #### Referenced at: 1. [findEthPerToken()](./pricing.ts#findethpertoken) 2. [bigDecimalExponated()](./index.ts#bigdecimalexponated) 3. [createTick()](./tick.ts#createtick) ### BI_18 ``` - type: BigInt - value: 18 ``` ### factoryContract An object representing a smart contract based on `abis/factory.json` abi, binded to `FACTORY_ADDRESS` to query the smart contract data. #### Dependencies: 1. [FACTORY_ADDRESS](#factory_address) #### Referenced at: 1. [getPosition()](../mappings/position-manager.ts#getposition) --- ## index.ts path: [`/src/utils/index.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/utils/index.ts) ### exponentToBigDecimal() ``` Params: - decimals (BigInt): The power of ten to return. ReturnType: BigDecimal ``` Returns the number `1` followed by `decimals` number of `0s` with type BigDecimal. It uses a for loop to iterate between `0` and `decimals` and multiplies the previous result by `10`. Thus, only positive values are possible. #### Dependencies: 1. [ZERO_BI](./constants.ts#zero_bi) 2. [ONE_BI](./constants.ts#one_bi) #### Invoked at: 1. [tokenAmountToDecimal()](#tokenamounttodecimal) 2. [priceToDecimal()](#pricetodecimal) 3. [convertTokenToDecimal()](#converttokentodecimal) 4. [convertEthToDecimal](#convertethtodecimal) 5. [sqrtPriceX96ToTokenPrices](./pricing.ts#sqrtpricex96totokenprices) ### safeDiv() ``` Params: - amount0 (BigDecimal): Numerator for the division - amount1 (BigDecimal): Denominator for the division ReturnType: BigDecimal ``` Return `0` if parameter `amount1` is equal to `ZERO_BD`. Else returns the result of dividing `amount0` by `amount1` using `BigDecimal`'s `div()` method. #### Dependencies: 1. [ZERO_BD](./constants.ts#zero_bd) #### Invoked at: 1. [bigDecimalExponated()](#bigdecimalexponated) 2. [priceToDecimal()](#pricetodecimal) 3. [createTick()](./tick.ts#createtick) ### bigDecimalExponated() ``` Params: - value (BigDecimal): value to be raised to a certain power - power (BigInt): the exponent of the value to be calculated ReturnType: BigDecimal ``` If `power` is `ZERO_BI`, `ONE_BD` is returned. `value` is multipled by itself in a simple for loop executed `abs(power)` number of times. If the `power` is negative, uses `safeDiv` to divide `ONE_BD` with the result of the previous calculation. Returns the result in BigDecimal. #### Dependencies: 1. [ZERO_BI](./constants.ts#zero_bi) 2. [ZERO_BD](./constants.ts#zero_bd) 3. [ONE_BI](./constants.ts#one_bi) 4. [ONE_BD](./constants.ts#one_bd) 5. [safeDiv()](#safediv) #### Invoked at: 1. [createTick()](./tick.ts#createtick) - Differs in logic to compute the exponent from other chains. - Instead of looping and multiplying `value` through `power` loop iterations, performs [simple exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring) #### Additional Dependencies: 1. [TWO_BI](./constants.ts#two_bi) ### tokenAmountToDecimal() ``` Params: - tokenAmount (BigDecimal): The amount of tokens to be divided (numerator) - exchangeDecimals (BigInt): The power of 10 to divide the amount with ReturnType: BigDecimal ``` If exchangeDecimals is `ZERO_BI`, returns tokenAmount after converting to BigDecimal. Else divides the BigDecimal tokenAmount using 10 raised to `exchangeDecimals` as the denominator. #### Dependencies: 1. [ZERO_BI](./constants.ts#zero_bi) 2. [exponentToBigDecimal()](#exponenttobigdecimal) #### Invoked at: 1. [] ### priceToDecimal() ``` Params: - amount (BigDecimal): The amount to be divided (numerator) - exchangeDecimals (BigInt): The power of 10 to divide the amount with ReturnType: BigDecimal ``` If `exchangeDecimals` is equal to `ZERO_BI` returns the amount as it is. Otherwise uses `safeDiv` to divide `amount` with `10^exchangeDecimals` in BigDecimals type. #### Dependencies: 1. [ZERO_BI](./constants.ts#zero_bi) 2. [safeDiv()](#safediv) 3. [exponentToBigDecimal()](#exponenttobigdecimal) #### Invoked at: 1. ### equalToZero() ``` Params: - value (BigDecimal): Value to check whether zero ReturnType: boolean ``` Converts value to string and then to float. Compares it against ZERO_BD after converting to String and then parsing as float. Returns boolean value from comparing the equality of the two float values. #### Dependencies: 1. [ZERO_BD](./constants.ts#zero_bd) #### Invoked at: 1. ### isNullEthValue() ``` Params: - value (String) - Hex String to check for Null Eth value ReturnType: boolean ``` Returns boolean value. True is value == '0x0000000000000000000000000000000000000000000000000000000000000001', else false. #### Invoked at: 1. [fetchTokenSymbol()](./token.ts#fetchtokensymbol) 2. [fetchTokenName()](./token.ts#fetchtokenname) ### bigDecimalExp18() ``` ReturnType: BigDecimal Value: 10^18 ``` #### Invoked at: 1. ### convertTokenToDecimal() ``` Params: - tokenAmount (BigInt) - The amount of token value to be converted - exchangeDecimals (BigInt) - The positive power of the exponent to divide the tokenAmount with ReturnType: BigDecimal ``` If exchangeDecimals is `ZERO_BI`, returns tokenAmount after converting to BigDecimal. Else divides the BigDecimal tokenAmount using 10 raised to `exchangeDecimals` as the denominator. #### Dependencies: 1. [ZERO_BI](./constants.ts#zero_bi) 2. [exponentToBigDecimal](#exponenttobigdecimal) #### Invoked at: 1. [handleMint()](../mappings/core.ts#handlemint) 2. [handleBurn()](../mappings/core.ts#handleburn) 3. [handleSwap()](../mappings/core.ts#handleswap) 4. [handleIncreaseLiquidity()](../mappings/position-manager.ts#handleincreaseliquidity) 5. [handleDecreaseLiquidity()](../mappings/position-manager.ts#handledecreaseliquidity) 6. [handleCollect()](../mappings/position-manager.ts#handlecollect) #### Additionally Invoked At: 1. [populateEmptyPools()](./backfill.ts#populateemptypools) ### convertEthToDecimal() ``` Params: - eth (BigInt) - Int value representing ether amount in wei ReturnType: BigDecimal ``` Converts the value of ether in wei from integer to big decimal representing amount in ether. It converts the eth parameter to BigDecimal and then divides it with 10^18 BigDecimal value. #### Dependencies: 1. [exponentToBigDecimal](#exponenttobigdecimal) #### Invoked at: 1. ### loadTransaction() ``` Params: - event (ethereum.Event) - Ethereum event emitted from the transaction to return. ReturnType: Transaction ``` Returns a [Transaction](../../schemas/transaction.md) instance for the specified event. If a transaction instance doesn't already exit for the event, it's created and then returned. Uses `event.transaction.hash.toHexString()` to find the relevant transaction or to create a new transaction instance. Uses `event` parameters `block.blockNumber`, `block.timestamp`, `transaction.gasUsed` and `transaction.gasPrice` to populate `transaction`'s fields. #### Entites: 1. [Transaction](../../schemas/transaction.md) - Read/Create & Write #### Invoked at: 1. [getPosition()](../mappings/position-manager.ts#getposition) 2. [savePositionSnapshot()](../mappings/position-manager.ts#savepositionsnapshot) 3. [handleMint()](../mappings/core.ts#handlemint) 4. [handleBurn()](../mappings/core.ts#handleburn) 5. [handleSwap()](../mappings/core.ts#handleswap) --- ## intervalUpdates.ts path: [`/src/utils/intervalUpdates.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/utils/intervalUpdates.ts) ### updateUniswapDayData() ``` Params: - event (ethereum.Event): The event used to determine dayId ReturnType: UniswapDayData ``` Updates the `tvlUSD` and `txCount` parameters for `UniswapDayData` entity for a given day. Sets the variables to `factory` entity's `totalValueLockedUSD` and `txCount` values respectively. Uses `event.block.timestamp.toI32() / 86400` to determine the `dayID` for `UniswapDayData`. If a `UniswapDayData` entity for the given day doesn't exist already, it is first created, with rest of the metrics initialized to `ZERO_BD`. #### Entities: 1. [Factory](../../schemas/factory) - Read 2. [UniswapDayData](../../schemas/uniswapDayData) - Read/Create & Write #### Dependencies: 1. [ZERO_BD](./constants.ts#zero_bd) #### Invoked at: 1. [handleMint()](../mappings/core.ts#handlemint) 2. [handleBurn()](../mappings/core.ts#handleburn) 3. [handleSwap()](../mappings/core.ts#handleswap) ### updatePoolDayData() ``` Params: - event (ethereum.Event): The event used to determine pool address and dayId to get the PoolId ReturnType: PoolDayData ``` Using `event.block.timestamp.toI32()/86400` and `event.address.toHexString()` to get the `dayID` and `poolID` respectively. Together also give the `poolDayDataID`. Creates a new `PoolDayData` entity for the day if not found. The new entity's metrics are initialized to `ZERO_BD` or `ZERO_BI`, while the `open`, `high`, `low` and `close` values are set to `pool.token0price`. Updates the `PoolDayData` values `high` or `low` conditionally by comparing `pool.token0Price` with the existing values. Updates the rest of the metrics using values from `Pool` entity. Note: Currently updates `poolDayData.close` only when a new entity is created. #### Entities: 1. [Pool](../../schemas/pool) - Read 2. [PoolDayData](../../schemas/poolDayData) - Read/Create & Write #### Dependencies: 1. [ZERO_BD](./constants.ts#zero_bd) 2. [ZERO_BI](./constants.ts#zero_bi) #### Invoked at: 1. [handleInitialize()](../mappings/core.ts#handleinitialize) 2. [handleMint()](../mappings/core.ts#handlemint) 3. [handleBurn()](../mappings/core.ts#handleburn) 4. [handleSwap()](../mappings/core.ts#handleswap) - Uses the logic of mainnet but doesn't initialize or update `poolDayData.feeGrowthGlobal0X128` and `poolDayData.feeGrowthGlobal1X128` values. ### updatePoolHourData() ``` Params: - event (ethereum.Event): The event used to determine pool address and hourId to get the HourPoolId ReturnType: PoolHourData ``` Using `event.block.timestamp.toI32()/3600` and `event.address.toHexString()` to get the `hourIndex` and `poolID` respectively. Together also give the `hourPoolID`. Creates a new `PoolHourData` entity for the specific hour if not found. The new entity's metrics are initialized to `ZERO_BD` or `ZERO_BI`, while the `open`, `high`, `low` and `close` values are set to `pool.token0price`. Updates the `PoolHourData` values `high` or `low` conditionally by comparing `pool.token0Price` with the existing values. Updates the rest of the metrics using values from `Pool` entity. #### Entities: 1. [Pool](../../schemas/pool) - Read 2. [PoolHourData](../../schemas/poolHourData) - Read/Create & Write #### Dependencies: 1. [ZERO_BD](./constants.ts#zero_bd) 2. [ZERO_BI](./constants.ts#zero_bi) 3. [ONE_BI](./constants.ts#one_bi) #### Invoked at: 1. [handleInitialize()](../mappings/core.ts#handleinitialize) 2. [handleMint()](../mappings/core.ts#handlemint) 3. [handleBurn()](../mappings/core.ts#handleburn) 4. [handleSwap()](../mappings/core.ts#handleswap) - Uses the logic of mainnet but doesn't initialize or update `poolHourData.feeGrowthGlobal0X128` and `poolHourData.feeGrowthGlobal1X128` values. ### updateTokenDayData() ``` Params: - token (Token): token to update the daily metrics for - event (ethereum.Event): The event used to determine the dayID ReturnType: TokenDayData ``` Uses `event.block.timestamp.toI32() / 86400` to determine the `dayID`. Uses `dayId` and `token.id.toString()` to get the `tokenDayID`. Uses `token`'s value in terms of eth and multiplies it with eth's price in USD using `bundle.ethPriceUSD` to get `tokenPrice`. Creates a new `TokenDayData` entity if one for `tokenDayID` is not found. Initializes the metrics to `ZERO_BD`, while the `open`, `high`, `low` and `close` are set to `tokenPrice`. Updates the `TokenDayData` values `high` or `low` conditionally by comparing `pool.token0Price` with the existing values. Upadates the rest of the metrics using valus from `Token` entity. #### Entities: 1. [Bundle](../../schemas/bundle) - Read 2. [TokenDayData](../../schemas/tokenDayData) - Read/Create & Write #### Dependencies: 1. [ZERO_BD](./constants.ts#zero_bd) #### Invoked at: 1. [handleMint()](../mappings/core.ts#handlemint) 2. [handleBurn()](../mappings/core.ts#handleburn) 3. [handleSwap()](../mappings/core.ts#handleswap) ### updateTokenHourData() ``` Params: - token (Token): token to update hourly metrics for - event (ethereum.Event): The event used to determine the hourIndex ReturnType: TokenHourData ``` Using `event.block.timestamp.toI32()/3600` to get the `hourIndex`. Uses `hourIndex` and `token.id.toString()` to get the `tokenHourID`. Uses `token`'s value in terms of eth and multiplies it with eth's price in USD using `bundle.ethPriceUSD` to get `tokenPrice`. Creates a new `TokenHourData` entity for the specific hour if not found. The new entity's metrics are initialized to `ZERO_BD`, while the `open`, `high`, `low` and `close` values are set to `tokenPrice`. Updates the `TokenHourData` values `high` or `low` conditionally by comparing `tokenPrice` with the existing values. Updates the rest of the metrics using values from `token` entity. #### Entities: 1. [Bundle](../../schemas/bundle) - Read 2. [TokenHourData](../../schemas/tokenHourData) - Read/Create & Write #### Dependencies: 1. [ZERO_BD](./constants.ts#zero_bd) #### Invoked at: 1. [handleMint()](../mappings/core.ts#handlemint) 2. [handleBurn()](../mappings/core.ts#handleburn) 3. [handleSwap()](../mappings/core.ts#handleswap) ### updateTickDayData() ``` Params: - tick (Tick): Tick to update daily metrics for - event (ethereum.Event): The event used to determine the dayId ReturnType: TickDayData ``` Uses `event.block.timestamp.toI32() / 86400` to determine the `dayID`. Uses `dayId` and `tick.id` to get the `tickDayDataID`. Creates a new `TickDayData` entity for the specific day if not found. Initializes the fields `pool` and `tick` using `tick.pool` and `tick.id` respectively. Sets the `TickDayData` entity's field values using corresponding fields from `tick`. #### Entities: 1. [TickDayData](../../schemas/tickDayData) - Read/Create & Write #### Invoked at: 1. [updateTickFeeVarsAndSave()](../mappings/core.ts#updatetickfeevarsandsave) - Uses the logic of mainnet but doesn't initialize or update `tickDayData.feeGrowthOutside0X128` and `tickDayData.feeGrowthOutside1X128` values. --- ## pricing.ts path: [`/src/utils/pricing.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/utils/pricing.ts) ### WETH_ADDRESS ``` - type: string - value: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' ``` ``` - type: string - value: '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619' ``` ``` - type: string - value: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' ``` ``` - type: string - value: '0x4200000000000000000000000000000000000006' ``` Address of wrapped-ETH (WETH) contract on ethereum mainnet. #### Referenced at: 1. [WHITELIST_TOKENS](#whitelist_tokens) 2. [findEthPerToken()](#findethpertoken) ### USDC_WETH_03_POOL ``` - type: string - value: '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8' ``` Address of Uniswap V3 pool contract between `USDC` and `WETH` `ERC-20` tokens on the specific chain. #### Referenced at: 1. [getEthPriceInUSD](#getethpriceinusd) ``` - type: string - value: '0x0e44ceb592acfc5d3f09d996302eb4c499ff8c10' ``` ``` - type: string - value: '0x17c14d2c404d167802b16c450d3c99f88f2c4f4d' ``` - Not present. Instead refer [DAI_WETH_03_POOL](#dai_weth_03_pool) ### DAI_WETH_03_POOL ``` - type: string - value: '0x03af20bdaaffb4cc0a521796a223f7d85e2aac31' ``` Address of Uniswap V3 pool contract between `DAI` and `WETH` `ERC-20` tokens on optimsim mainnet. #### Referenced at: 1. [getEthPriceInUSD](#getethpriceinusd) - Not present. Instead refer [USDC_WETH_03_POOL](#usdc_weth_03_pool) ### WHITELIST_TOKENS A list of tokens which have considerable usage and are likely to have pool pairing with other tokens. These can be used for calculating liquidity in USD by using the tokens price in USD. The following token addresses are present in the list: |Symbol|Address| |-|-| |WETH|`WETH_ADDRESS`*| |DAI|[0x6b175474e89094c44da98b954eedeac495271d0f](https://etherscan.io/address/0x6b175474e89094c44da98b954eedeac495271d0f)| |USDC|[0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48)| |USDT|[0xdac17f958d2ee523a2206206994597c13d831ec7](https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7)| |TUSD|[0x0000000000085d4780b73119b644ae5ecd22b376](https://etherscan.io/address/0x0000000000085d4780b73119b644ae5ecd22b376)| |WBTC|[0x2260fac5e5542a773aa44fbcfedf7c193bc2c599](https://etherscan.io/address/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599)| |cDAI|[0x5d3a536e4d6dbd6114cc1ead35777bab948e3643](https://etherscan.io/address/0x5d3a536e4d6dbd6114cc1ead35777bab948e3643)| |cUSDC|[0x39aa39c021dfbae8fac545936693ac917d5e7563](https://etherscan.io/address/0x39aa39c021dfbae8fac545936693ac917d5e7563)| |EBASE|[0x86fadb80d8d2cff3c3680819e4da99c10232ba0f](https://etherscan.io/address/0x86fadb80d8d2cff3c3680819e4da99c10232ba0f)| |sUSD|[0x57ab1ec28d129707052df4df418d58a2d46d5f51](https://etherscan.io/address/0x57ab1ec28d129707052df4df418d58a2d46d5f51)| |MKR|[0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2](https://etherscan.io/address/0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2)| |COMP|[0xc00e94cb662c3520282e6f5717214004a7f26888](https://etherscan.io/address/0xc00e94cb662c3520282e6f5717214004a7f26888)| |LINK|[0x514910771af9ca656af840dff83e8264ecf986ca](https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca)| |SNX|[0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f](https://etherscan.io/address/0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f)| |YFI|[0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e](https://etherscan.io/address/0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e)| |1INCH|[0x111111111117dc0aa78b770fa6a738034120c302](https://etherscan.io/address/0x111111111117dc0aa78b770fa6a738034120c302)| |yCurv|[0xdf5e0e81dff6faf3a7e52ba697820c5e32d806a8](https://etherscan.io/address/0xdf5e0e81dff6faf3a7e52ba697820c5e32d806a8)| |FEI|[0x956f47f50a910163d8bf957cf5846d573e7f87ca](https://etherscan.io/address/0x956f47f50a910163d8bf957cf5846d573e7f87ca)| |MATIC|[0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0](https://etherscan.io/address/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0)| |AAVE|[0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9](https://etherscan.io/address/0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9)| |sETH2|[0xfe2e637202056d30016725477c5da089ab0a043a](https://etherscan.io/address/0xfe2e637202056d30016725477c5da089ab0a043a)| #### Dependencies: 1. [WETH_ADDRESS](#weth_address) #### Referenced at: 1. [getTrackedAmountUSD](#gettrackedamountusd) 2. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) |Symbol|Address| |-|-| |WETH|`WETH_ADDRESS`*| |WMATIC|[0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270](https://polygonscan.com/address/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270)| |USDC|[0x2791bca1f2de4661ed88a30c99a7a9449aa84174](https://polygonscan.com/address/0x2791bca1f2de4661ed88a30c99a7a9449aa84174)| |DAI|[0x8f3cf7ad23cd3cadbd9735aff958023239c6a063](https://polygonscan.com/address/0x8f3cf7ad23cd3cadbd9735aff958023239c6a063)| |Symbol|Address| |-|-| |WETH|`WETH_ADDRESS`*| |USDC|[0xff970a61a04b1ca14834a43f5de4533ebddb5cc8](https://arbiscan.io/address/0xff970a61a04b1ca14834a43f5de4533ebddb5cc8)| |DAI|[0xda10009cbd5d07dd0cecc66161fc93d7c9000da1](https://arbiscan.io/address/0xda10009cbd5d07dd0cecc66161fc93d7c9000da1)| |USDT|[0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9](https://arbiscan.io/address/0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9)| |Symbol|Address| |-|-| |WETH|`WETH_ADDRESS`*| |DAI|[0xda10009cbd5d07dd0cecc66161fc93d7c9000da1](https://optimistic.etherscan.io/address/0xda10009cbd5d07dd0cecc66161fc93d7c9000da1)| |OP|[0x4200000000000000000000000000000000000042](https://optimistic.etherscan.io/address/0x4200000000000000000000000000000000000042)| |USDC|[0x7f5c764cbc14f9669b88837ca1490cca17c31607](https://optimistic.etherscan.io/address/0x7f5c764cbc14f9669b88837ca1490cca17c31607)| |PERP|[0x9e1028f5f1d5ede59748ffcee5532509976840e0](https://optimistic.etherscan.io/address/0x9e1028f5f1d5ede59748ffcee5532509976840e0)| |LYRA|[0x50c5725949a6f0c72e6c4a641f24049a917db0cb](https://optimistic.etherscan.io/address/0x50c5725949a6f0c72e6c4a641f24049a917db0cb)| |USDT|[0x94b008aa00579c1307b0ef2c499ad98a8ce58e58](https://optimistic.etherscan.io/address/0x94b008aa00579c1307b0ef2c499ad98a8ce58e58)| |WBTC|[0x68f180fcce6836688e9084f035309e29bf0a2095](https://optimistic.etherscan.io/address/0x68f180fcce6836688e9084f035309e29bf0a2095)| #### Additional Referenced at: 1. [populateEmptyPools()](./backfill.ts#populateemptypools) \* -> The value is imported from a variable and listed directly in the list declaration. ### STABLE_COINS A list of ERC20 token contract addresses which have stable coin prices, i.e., 1 token expected to be valued at 1 USD. ||Address| |-|-| |DAI|[0x6b175474e89094c44da98b954eedeac495271d0f](https://etherscan.io/address/0x6b175474e89094c44da98b954eedeac495271d0f)| |USDC|[0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48)| |USDT|[0xdac17f958d2ee523a2206206994597c13d831ec7](https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7)| |TUSD|[0x0000000000085d4780b73119b644ae5ecd22b376](https://etherscan.io/address/0x0000000000085d4780b73119b644ae5ecd22b376)| |FEI|[0x956f47f50a910163d8bf957cf5846d573e7f87ca](https://etherscan.io/address/0x956f47f50a910163d8bf957cf5846d573e7f87ca)| |PRINTS|[0x4dd28568d05f09b02220b09c2cb307bfd837cb95](https://etherscan.io/address/0x4dd28568d05f09b02220b09c2cb307bfd837cb95)| #### Referenced at: 1. [findEthPertoken()](#findethpertoken) ||Address| |-|-| |USDC|[0x2791bca1f2de4661ed88a30c99a7a9449aa84174](https://polygonscan.com/address/0x2791bca1f2de4661ed88a30c99a7a9449aa84174)| - `STABLE_COINS` is not defined for arbitrum-one subgraph. - `STABLE_COINS` is not defined for optimism subgraph. ### MINIMUM_ETH_LOCKED ``` - type: BigDecimal - value: 60 ``` While calculating token price in USD, the value of other token locked in the pool in terms of eth has to be greated than `MINIMUM_ETH_LOCKED`. #### Referenced at: 1. [findEthPertoken()](#findethpertoken) ``` - type: BigDecimal - value: 5 ``` ``` - type: BigDecimal - value: 4 ``` #### Additionally Referenced at: 1. [getEthPriceInUSD()](#getethpriceinusd) ``` - type: BigDecimal - value: 10 ``` ### sqrtPriceX96ToTokenPrices() ``` Params: - sqrtPriceX96 (BigInt): The square root of the price of token1 in terms of token0 in Q64.96 format. Formula: sqrt(token0.price/token1.price)*(2^96) - token0 (Token): The first token in the pool pair to calculate the relative price for - token1 (Token): The second token in the pool pair to calculate the relative price for ReturnType: BigDecimal[] ``` Find the price of `token0` and `token1` in the pool relative to each other and returns the two prices. #### Formula: ``` num = (sqrtPriceX96^2) # Squaring the root to get the price denom = 2^192 # To divide price by 96^2 to convert the Q64.96 number to BigDecimal price1 = ((num/denom) * (10^token0.decimals))/ (10^token1.decimals) # Calculating price1 price0 = 1/price1 ``` #### Dependencies: 1. [exponentToBigDecimal()](./index.ts#exponenttobigdecimal) 2. [safeDiv()](./index.ts#safediv) #### Invoked at: 1. [handleSwap()](../mappings/core.ts#handleswap) ### getEthPriceInUSD() ``` Params: none ReturnType: BigDecimal ``` Returns the Price of ETH in terms of USD, based on the stable coin pools. Currently, the `token0Price` for the `pool` represented by `USDC_WETH_03_POOL`. When `pool` entity is not found, returns `ZERO_BD`. #### Entites: 1. [Pool](../../schemas/pool.md) - Read Entity #### Dependencies: 1. [USDC_WETH_03_POOL](#usdc_weth_03_pool) 2. [ZERO_BD](./constants.ts#zero_bd) #### Invoked at: 1. [handleInitialize()](../mappings/core.ts#handleinitialize) 2. [handleSwap()](../mappings/core.ts#handleSwap) - Logic same as mainnet, except returns `ZERO_BD` if eth locked is not greated than `MINIMUM_ETH_LOCKED`. #### Additional Dependencies: 1. [MINIMUM_ETH_LOCKED](#minimum_eth_locked) - Uses [DAI_WETH_03_POOL](#dai_weth_03_pool) instead of USDC #### Differing Dependencies: 1. [DAI_WETH_03_POOL](#dai_weth_03_pool) ### findEthPerToken() ``` Params: - token (Token): Token entity to find the price in terms of ETH ReturnType: BigDecimal ``` If token is weth, returns 1. If token in `STABLE_COINS`, returns `1/bundle.ethPriceUSD`. Else, iterates over all the whitelisted pools for the token using `token.whitelistPools`. Finds the pool with largest liquidity value in terms of ETH, as long as the value is atleast `MINIMUM_ETH_LOCKED`. Uses the eth value of the paired token and relative token price between the token pair to find the `token`'s' value in terms of eth. If there's no whitelisted pool with `MINIMUM_ETH_LOCKED`, returns `ZERO_BD`. #### Entites: 1. [Bundle](../../schemas/bundle.md) - Read Entity 2. [Pool](../../schemas/pool.md) - Read Entity 3. [Token](../../schemas/token.md) - Read Entity #### Dependencies: 1. [WETH_ADDRESS](#weth_address) 2. [ONE_BD](./constants.ts#one_bd) 3. [ZERO_BD](./constants.ts#zero_bd) 4. [STABLE_COINS](#stable_coins) 5. [MINIMUM_ETH_LOCKED](#minimum_eth_locked) #### Invoked at: 1. [handleInitialize()](../mappings/core.ts#handleinitialize) 2. [handleSwap()](../mappings/core.ts#handleSwap) - Doesn't check if token is present in `STABLE_COINS`. Rest of the logic is same as mainnet. ### getTrackedAmountUSD() ``` Params: - tokenAmount0 (BigDecimal): - token0 (Token0): - tokenAmount1 (BigDecimal): - token1 (Token): ReturnType: BigDecimal ``` Returns the USD value equivalent to `tokenAmoun0` and `tokenAmount1` together. Calculates the USD price using `token.derviedEth*bundle.ethPriceUSD` as the multiplier if the token is present in `WHITELIST_TOKENS`. If both the tokens are present, it adds their individual USD prices. If only one is present, it uses 2X the value of that token. If neither are in the `WHITELIST_TOKENS` list, returns `ZERO_BD`. #### Entites: 1. [Bundle](../../schemas/bundle.md) - Read Entity #### Dependencies: 1. [WHITELIST_TOKENS](#whitelist_tokens) 2. [ZERO_BD](./constants.ts#zero_bd) #### Invoked at: 1. [handleSwap()](../mappings/core.ts#handleSwap) --- ## staticTokenDefinition.ts path: [`/src/utils/staticTokenDefinition.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/utils/staticTokenDefinition.ts) ## Class StaticTokenDefinition A utility class to represent an `ERC20` token metadata. Contains four fields: |Field|Type| |-|-| |address|Address| |symbol|string| |name|string| |decimals|BigInt| It has a constructor to initialized an object: ``` constructor(address: Address, symbol: string, name: string, decimals: BigInt) ``` The class exposes two static functions: 1. [getStaticDefinitions()](#getstaticdefinitions) 2. [fromAddress()](#fromaddress) ### getStaticDefinitions() Returns an `Array` object with the token definitions defined with the function: |Address|Symbol|Name|Decimals| |-|-|-|-| |0xe0b7927c4af23765cb51314a0e0521a9645f0e2a|DGD|DGD|9| |0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9|AAVE|Aave Token|18| |0xeb9951021698b42e4399f9cbb6267aa35f82d59d|LIF|Lif|18| |0xbdeb4b83251fb146687fa19d1c660f99411eefe3|SVD|savedroid|18| |0xbb9bc244d798123fde783fcc1c72d3bb8c189413|TheDAO|TheDAO|16| |0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2|HPB|HPBCoin|18| |Address|Symbol|Name|Decimals| |-|-|-|-| |0x82af49447d8a07e3bd95bd0d56f35241523fbab1|WETH|Wrapped Ethereum|18| |0xff970a61a04b1ca14834a43f5de4533ebddb5cc8|USDC|USD Coin|6| |Address|Symbol|Name|Decimals| |-|-|-|-| |0x82af49447d8a07e3bd95bd0d56f35241523fbab1|WETH|Wrapped Ethereum|18| #### Invoked at: 1. [fromAddress()](#fromaddress) ### fromAddress() ``` Params: - tokenAddress (Address): the ERC20 address to search for in the ERC20 symbols defined in StaticTokenDefinition class ReturnType: StaticTokenDefinition | null ``` Get an Array of `StaticTokenDefinition` objects from static method getStaticDefinition() and iterates through them to find the `tokenAddress`, If found returns the `StaticTokenDefinition` object, else returns `null`. #### Dependencies: 1. [getStaticDefinitions()](#getstaticdefinitions) #### Invoked at: 1. [fetchTokenSymbol()](./token.ts#fetchtokensymbol) 2. [fetchTokenName()](./token.ts#fetchtokenname) 3. [fetchTokenDecimals()](./token.ts#fetchtokendecimals) --- ## tick.ts path: [`/src/utils/tick.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/utils/tick.ts) ### createTick() ``` Params: - tickId (String): ID of the tick instance to create. Format: # - tickIdx (i32): Tick index - poolId (string): PoolId - event (MintEvent): The mint event where liquidity was added to the tick ReturnType: Tick ``` Initializes a new Tick to store the liquidity present at the specific tick. Sets `tick.id`, `tick.tickIdx`, `tick.pool` and `tick.poolId` from the parametrs. Sets `tick.creatdAtTimeStamp` and `tick.createdAtBlockNumber` from `event.block.timestamp` and `event.block.number` respectively. `tick.price0` is calcualted as `1.0001^tickIdx` and `tick.price1` as `safeDiv(ONE_BD, price0)`. All the other parameters are initialized to `ZERO_BD` or `ZERO_BI`. #### Entites: 1. [Tick](../../schemas/tick.md) - Create #### Dependencies: 1. [ZERO_BI](./constants.ts#zero_bi) 2. [ONE_BD](./constants.ts#one_bd) 3. [ZERO_BD](./constants.ts#zero_bd) 4. [bigDecimalExponated()](./index.ts#bigdecimalexponated) 5. [safeDiv()](./index.ts#safediv) #### Invoked at: 1. [handleMint()](../mappings/core.ts#handlemint) - Logic same as mainnet, except doesn't initialize the variables `tick.feeGrowthOutside0X128` and `tick.feeGrowthOutside1X128` ### createTickBurn() :::info Only in Optimism This function exists only in optimism subgraph ::: ``` Params: - tickId (String): ID of the tick instance to initialize. Format: # - tickIdx (i32): Tick index - poolId (string): PoolId - event (MintEvent): The event where the liquidity from the tick is removed ReturnType: Tick ``` Instantiate a tick that already exists from previous transactions. - Uses the same logic as [createTick()](#createtick) to initialize a new tick entity - Later reads the Pool contract ticks data for `tickIdx` and sets the `tick.liquidityGross`, `tick.liquidityNet`, `tick.feeGrowthOutside0X128` and `tick.feeGrowthOutside1X128` vlaues. #### Entites: 1. [Tick](../../schemas/tick.md) - Create * Write #### ABI Dependencies: 1. pool.json #### Dependencies: 1. [ZERO_BI](./constants.ts#zero_bi) 2. [ONE_BD](./constants.ts#one_bd) 3. [ZERO_BD](./constants.ts#zero_bd) 4. [bigDecimalExponated()](./index.ts#bigdecimalexponated) 5. [safeDiv()](./index.ts#safediv) #### Invoked at: 1. [handleBurn()](../mappings/core.ts#handleburn) ### feeTierToTickSpacing() ``` Params: - feeTier (BigInt): The fee tier specified for the pool ReturnType: BigInt ``` Given a specific fee tier, returns a BigInt value for the respective tick spacing used in the pool contract. |Fee Tier|TickSpaceing Returned| |--|---| | 10000 | 200 | | 3000 | 60 | | 500 | 10 | | 100 | 1 | | Anything Else | Error: 'Unexpected fee tier'| #### Invoked at: 1. [handleSwap()](../mappings/core.ts#handleswap) - Doesn't have the Fee Tier `100`, Tick Space `1` entry. --- ## token.ts path: [`/src/utils/token.ts`](https://github.com/Uniswap/v3-subgraph/blob/main/src/utils/token.ts) ### fetchTokenSymbol() ``` Params: - tokenAddress (Address): The ERC20 Token Address for which the symbol is returned ReturnType: string ``` Returns the string value representing the ERC20 symbol read from the contract with address `tokenAddress` using `symbol()` method. If reverted, checks if the `tokenAddres` is present in `StaticTokenDefinition`. Returns `unknown` if not found. #### ABI Dependencies: 1. ERC20.json 2. ERC20SymbolBytes.json #### Dependencies: 1. [isNullEthValue()](./index.ts#isnullethvalue) 2. [StaticTokenDefintion](./staticTokenDefinition.ts#statictokendefinition) 3. [StaticTokenDefintion.fromAddress()](./staticTokenDefinition.ts#fromaddress) #### Invoked at: 1. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) - Logic similar to mainnet except uses `StaticTokenDefition` first and proceeds to read from contract if not found. - Logic similar to mainnet except uses `StaticTokenDefition` first and proceeds to read from contract if not found. #### Additionally Invoked At: 1. [populateToken()](./backfill.ts#populatetoken) ### fetchTokenName() ``` Params: - tokenAddress (Address): The ERC20 Token Address for which the Name is returned ReturnType: string ``` Returns the string value representing the ERC20 name read from the contract with address `tokenAddress` using `name()` method. If reverted, checks if the `tokenAddres` is present in `StaticTokenDefinition`. Returns `unknown` if not found. #### ABI Dependencies: 1. ERC20.json 2. ERC20NameBytes.json #### Dependencies: 1. [isNullEthValue()](./index.ts#isnullethvalue) 2. [StaticTokenDefintion](./staticTokenDefinition.ts#statictokendefinition) 3. [StaticTokenDefintion.fromAddress()](./staticTokenDefinition.ts#fromaddress) #### Invoked at: 1. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) - Logic similar to mainnet except uses `StaticTokenDefition` first and proceeds to read from contract if not found. - Logic similar to mainnet except uses `StaticTokenDefition` first and proceeds to read from contract if not found. #### Additionally Invoked At: 1. [populateToken()](./backfill.ts#populatetoken) ### fetchTokenTotalSupply() ``` Params: - tokenAddress (Address): The ERC20 Token Address for which the Total Supply is ReturnType: BigInt ``` Queries the ERC20 contract with address `tokenAddress` and returns total token supply using `totalSupply()` method. If call reverts, typecasts `null` to i32 then to BigDecimal and returns the value. #### ABI Dependencies: 1. ERC20.json #### Invoked at: 1. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) #### Additionally Invoked At: 1. [populateToken()](./backfill.ts#populatetoken) ### fetchTokenDecimals() ``` Params: - tokenAddress (Address): The ERC20 Token Address for which the number of decimals is returned ReturnType: BigInt ``` Queries the ERC20 contract with address `tokenAddress` and returns the decimals value for the token `decimals()` method. If call reverts, check for `tokenAddress` in `StaticTokenDefinition`. If not found, typecasts `null` to i32 then to BigDecimal and returns the value. #### ABI Dependencies: 1. ERC20.json #### Dependencies: 1. [StaticTokenDefintion](./staticTokenDefinition.ts#statictokendefinition) 2. [StaticTokenDefintion.fromAddress()](./staticTokenDefinition.ts#fromaddress) #### Invoked at: 1. [handlePoolCreated()](../mappings/factory.ts#handlepoolcreated) - Logic similar to mainnet except uses `StaticTokenDefition` first and proceeds to read from contract if not found. - Logic similar to mainnet except uses `StaticTokenDefition` first and proceeds to read from contract if not found. #### Additionally Invoked At: 1. [populateToken()](./backfill.ts#populatetoken) --- ## Introduction ## Uniswap V3 SubGraph Docs Welcome to exploring the data of [**Uniswap**](https://uniswap.org/), the leading EVM DEX. The docs help you understand data structure in the **SubGraphs** deployed for the dex on the [graph protocol](https://thegraph.com/). If you're completely new to the working of the V3 protocol, would recommend going through the [references](#references) below. ## Uniswap Foundation The docs was possible as I recieved a grant from [Uniswap Foundation](https://uniswapfoundation.mirror.xyz/). The foundation publishes [grant wishlist for RFPs](https://uniswap.notion.site/Uniswap-Foundation-Grants-Wish-List-3be614ba4e504b5caeee7b0159e64a42) regularly if you wish to get involved. ## Get In Touch For any updates or additions, you can raise a PR to the [docs repo](https://github.com/vintageplayer/uniswap-v3-subgraph-docs). Feel free to reach out on [Twitter](https://twitter.com/artsofbaniya) to discuss about web3 or any field, or just to keep up with what I'm building. --- ## Resources ### Repo Links: - [V3 Subgraph Repo](https://github.com/Uniswap/v3-subgraph/tree/main) - [V3 Core Contracts Repo](https://github.com/Uniswap/v3-core) - Mainly for factory and pool contracts - [V3 Periphery Contract Repo](https://github.com/Uniswap/v3-periphery) - Mainly for NonFungiblePositionManager Contract ### References - [Uniswap V3 Core Whitepaper](https://uniswap.org/whitepaper-v3.pdf) - [Liquidity Math in Uniswap V3](https://atiselsts.github.io/pdfs/uniswap-v3-liquidity-math.pdf) - [Uniswap V3 Development Book](https://uniswapv3book.com/) ### Misc - [Uniswap Hacker Getting Started Guide](https://uniswap.notion.site/uniswap/Uniswap-Hacker-Getting-Started-Guide-781b008a16c849c8bf4d9920744e77f5) - [Uniswap Docs](https://docs.uniswap.org/) ([Code Repo](https://github.com/Uniswap/docs/tree/main)) - [Uniswap Info Site](https://info.uniswap.org/#/) to see the pool and token stats using the subgraph - [V3 New Chain Deployment Guide](https://github.com/Uniswap/v3-new-chain-deployments) (Slightly Outdated) --- ## Bundle Entity to store the current Eth price in USD. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | ID to fetch a unique entity. (Only ID='1' is used.) | |ethPriceUSD | BigDecimal! | | Price of ETH in USD | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[findEthPerToken()](../functions-n-handlers/utils/pricing.ts#findethpertoken)||
:white_check_mark:
||| |[getTrackedAmountUSD()](../functions-n-handlers/utils/pricing.ts#gettrackedamountusd)||
:white_check_mark:
||| |[handlePoolCreated()](../functions-n-handlers/mappings/factory.ts#handlepoolcreated)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| |[handleInitialize()](../functions-n-handlers/mappings/core.ts#handleinitialize)|||
:white_check_mark:*
|
:white_check_mark:*
| |[handleMint()](../functions-n-handlers/mappings/core.ts#handlemint)||
:white_check_mark:
||| |[handleBurn()](../functions-n-handlers/mappings/core.ts#handleburn)||
:white_check_mark:
||| |[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleIncreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handleincreaseliquidity)||
:white_check_mark:*
||| |[handleDecreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handledecreaseliquidity)||
:white_check_mark:*
||| |[handleCollect()](../functions-n-handlers/mappings/position-manager.ts#handlecollect)||
:white_check_mark:*
||| |[updateTokenDayData()](../functions-n-handlers/utils/intervalupdates.ts#updatetokendaydata)||
:white_check_mark:
||| |[updateTokenHourData()](../functions-n-handlers/utils/intervalupdates.ts#updatetokenhourdata)||
:white_check_mark:
||| \* -> Varies across chains --- ## Burn Entity to stores the details of a burn event emitted while removing liquidity from a pool. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Burn Entity ID. Format: `#` | |transaction | [Transaction](./transaction)! | | [Transaction Entity](./transaction) in which the burn event was emitted | |pool | [Pool](./pool)! | | [Pool](./pool) in which the burn event was emitted | |token0 | [Token](./token)! | | token0 entity of the pool | |token1 | [Token](./token)! | | token1 entity of the pool | |timestamp | BigInt! | | Timestamp of the block in which which the burn event was emitted | |owner | Bytes! | | owner of the position to which the liquidity was burnt | |origin | Bytes! | | The EOA address that initiated the transaction | |amount | BigInt! | | Amount of liquidity burnt | |amount0 | BigDecimal! | | Amount of token0 burnt | |amount1 | BigDecimal! | | Amount of token1 burnt | |amountUSD | BigDecimal | | Burn value derived in USD based on available prices of tokens | |tickLower | BigInt! | | Lower tick of the position | |tickUpper | BigInt! | | Upper tick of the position | |logIndex | BigInt | | Order of the Burn event within the logs of the transaction | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[handleBurn()](../functions-n-handlers/mappings/core.ts#handleburn)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| --- ## Collect Entity to store details of a collect event emitted while removing tokens from a position. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Collect Entity ID. Format: `#` | |transaction | [Transaction](./transaction)! | | [Transaction Entity](./transaction) in which the collect event was emitted | |timestamp | BigInt! | | Timestamp of the block in which which the collect event was emitted | |pool | [Pool](./pool)! | | [Pool](./pool) in which the collect event was emitted | |owner | Bytes! | | owner of the position from which the tokens were collected | |amount0 | BigDecimal! | | Amount of token0 collected | |amount1 | BigDecimal! | | Amount of token1 collected | |amountUSD | BigDecimal | | Collect value derived in USD based on available prices of tokens | |tickLower | BigInt! | | Lower tick of the position | |tickUpper | BigInt! | | Upper tick of the position | |logIndex | BigInt | | Order of the Collect Event within the logs of the transaction | ## Referencing Functions :::danger Couldn't find any references to collect schema ::: --- ## Factory Entity to capture metrics for all the pools deployed by a specific [factory contract](../contracts/factory). ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Factory Contract address | Field Missing | |poolCount | BigInt! | | No. of pools created using the factory | |txCount | BigInt! | | No. of all the transactions through pools mananged by the factory | |totalVolumeUSD | BigDecimal! | | Total Volume all time in derived USD | |totalVolumeETH | BigDecimal! | | Total Volume all time in derived ETH | |totalFeesUSD | BigDecimal! | | Total Swap Fees all time in derived USD | |totalFeesETH | BigDecimal! | | Total Swap Fees all time in derived ETH | |untrackedVolumeUSD | BigDecimal! | | Total Volume all time, including less reliable USD values | |totalValueLockedUSD | BigDecimal! | | TVL derived in USD | |totalValueLockedETH | BigDecimal! | | TVL derived in ETH | |totalValueLockedUSDUntracked | BigDecimal! | | TVL including tokens with unreliable USD prices in USD | |totalValueLockedETHUntracked | BigDecimal! | | TVL including tokens with unreliable USD prices in ETH | |populated | Boolean | | Flag capturing if the new pool is populated | All except optimism | |owner | ID! | | Current Owner of the factory contract | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[handlePoolCreated()](../functions-n-handlers/mappings/factory.ts#handlepoolcreated)|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleMint()](../functions-n-handlers/mappings/core.ts#handlemint)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleBurn()](../functions-n-handlers/mappings/core.ts#handleburn)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| [handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[updateUniswapDayData()](../functions-n-handlers/utils/intervalUpdates.ts#updateuniswapdaydata)||
:white_check_mark:
||| --- ## Flash Entity to store details of a flash event emitted while a flash loan was taken from a pool. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Flash Entity ID. Format: `#` | |transaction | [Transaction](./transaction)! | | [Transaction Entity](./transaction) in which the flash event was emitted | |timestamp | BigInt! | | Timestamp of the block in which which the flash event was emitted | |pool | [Pool](./pool)! | | [Pool](./pool) in which the flash event was emitted | |sender | Bytes | | the address of the sender that invoked the flash operation | |recipient | Bytes! | | the address of the recipient that received the flash amount | |amount0 | BigDecimal! | | Amount of token0 flashed | |amount1 | BigDecimal! | | Amount of token1 flashed | |amountUSD | BigDecimal | | Flash value derived in USD based on available prices of tokens | |amount0Paid | BigDecimal! | | Amount of token0 paid for the flash operation | |amount1Paid | BigDecimal! | | Amount of token1 paid for the flash operation | |logIndex | BigInt | | Order of the Flash Event within the logs of the transaction | ## Referencing Functions :::danger Couldn't find any references to collect schema ::: --- ## Mint Entity to store details of a mint event emitted while adding liquidity to a pool. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Mint Entity ID. Format: `#` | |transaction | [Transaction](./transaction)! | | [Transaction Entity](./transaction) in which the mint event was emitted | |timestamp | BigInt! | | Timestamp of the block in which which the mint event was emitted | |pool | [Pool](./pool)! | | [Pool](./pool) in which the mint event was emitted | |token0 | [Token](./token)! | | token0 entity of the pool | |token1 | [Token](./token)! | | token1 entity of the pool | |owner | Bytes! | | owner of the position to which the liquidity was minted | |sender | Bytes | | the address that minted the liquidity | |origin | Bytes! | | The EOA address that initiated the transaction | |amount | BigInt! | | Amount of liquidity minted | |amount0 | BigDecimal! | | Amount of token0 minted | |amount1 | BigDecimal! | | Amount of token1 minted | |amountUSD | BigDecimal | | Mint value derived in USD based on available prices of tokens | |tickLower | BigInt! | | Lower tick of the position | |tickUpper | BigInt! | | Upper tick of the position | |logIndex | BigInt | | Order of the Mint Event within the logs of the transaction | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[handleMint()](../functions-n-handlers/mappings/core.ts#handlemint)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| --- ## Pool Entity to store a pool's metadata, current & lifetime metrics and links to events and hourly/daily metrics and references to it's tick entities. ## Schema |Field|Type|derivedFrom|Description|Field Missing| |-|-|-|-|-| |id | ID! | | Pool Contract Address | |createdAtTimestamp | BigInt! | | BlockTime where the pool was created | |createdAtBlockNumber | BigInt! | | Block Number where the pool was created | |token0 | [Token](./token)! | | Token entity for token0 | |token1 | [Token](./token)! | | Token entity for token1 | |feeTier | BigInt! | | The percentage of token taken as fee in terms of basis points | |liquidity | BigInt! | | Liquidity in the currently active tick range | |sqrtPrice | BigInt! | | The current token pair price tracked in square root & Q96 format | |feeGrowthGlobal0X128 | BigInt! | | The total fee in token0 per unit of liquidity collected by the pool | arbitrum-one | |feeGrowthGlobal1X128 | BigInt! | | The total fee in token1 per unit of liquidity collected by the pool | arbitrum-one | |token0Price | BigDecimal! | | Price of token0 in terms of token1 | |token1Price | BigDecimal! | | Price of token1 in terms of token0 | |tick | BigInt | | The current active [tick](./tick) | |observationIndex | BigInt! | | The current observation index (used to record historic prices) | |volumeToken0 | BigDecimal! | | Total volume of token0 swapped in the pool | |volumeToken1 | BigDecimal! | | Total volume of token1 swapped in the pool | |volumeUSD | BigDecimal! | | Total volume swapped in the pool in USD value | |untrackedVolumeUSD | BigDecimal! | | Total volume swapped in the pool (including values for unreliable USD pools) in USD value | |feesUSD | BigDecimal! | | Total fee collected in USD value | |txCount | BigInt! | | Total no. of transactions in the pool | |collectedFeesToken0 | BigDecimal! | | Total amount of token0 fee collected | |collectedFeesToken1 | BigDecimal! | | Total amount of token1 fee collected | |collectedFeesUSD | BigDecimal! | | Total amount of fee collected in USD value | |totalValueLockedToken0 | BigDecimal! | | Total amount of token0 locked across all ticks in the pool | |totalValueLockedToken1 | BigDecimal! | | Total amount of token1 locked across all ticks in the pool | |totalValueLockedETH | BigDecimal! | | Total value locked in the pool in ETH value | |totalValueLockedUSD | BigDecimal! | | Total value locked in the pool in USD value | |totalValueLockedUSDUntracked | BigDecimal! | | Total value locked in the pool (including tokens with unreliable USD values) in USD value | |liquidityProviderCount | BigInt! | | Used for detecting new exchanges. (Currently not updated Anywhere) | |poolHourData | [[PoolHourData](./poolhourdata)!]! | @derivedFrom(field: "pool") | Hourly Snapshots of Pool's data | |poolDayData | [[PoolDayData](./pooldaydata)!]! | @derivedFrom(field: "pool") | Daily Snapshots of Pool's data | |mints | [[Mint](./mint)!]! | @derivedFrom(field: "pool") | Mint Events emitted from the Pool | |burns | [[Burn](./burn)!]! | @derivedFrom(field: "pool") | Burn Events emitted from the Pool | |swaps | [[Swap](./swap)!]! | @derivedFrom(field: "pool") | Swap Events emitted from the Pool | |collects | [[Collect](./collect)!]! | @derivedFrom(field: "pool") | Collect Events emitted from the Pool | |ticks | [[Tick](./tick)!]! | @derivedFrom(field: "pool") | Tick entities respresenting the Pool liquidity | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[handlePoolCreated()](../functions-n-handlers/mappings/factory.ts#handlepoolcreated)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| |[updatePoolDayData()](../functions-n-handlers/utils/intervalUpdates.ts#updatepooldaydata)||
:white_check_mark:
||| |[updatePoolHourData()](../functions-n-handlers/utils/intervalUpdates.ts#updatepoolhourdata)||
:white_check_mark:
||| |[getEthPriceInUSD()](../functions-n-handlers/utils/pricing.ts#getethpriceinusd)||
:white_check_mark:
||| |[findEthPerToken()](../functions-n-handlers/utils/pricing.ts#findethpertoken)||
:white_check_mark:
||| |[handleInitialize()](../functions-n-handlers/mappings/core.ts#handleinitialize)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:*
| |[handleMint()](../functions-n-handlers/mappings/core.ts#handlemint)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleBurn()](../functions-n-handlers/mappings/core.ts#handleburn)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleFlash()](../functions-n-handlers/mappings/core.ts#handleflash)|||
:white_check_mark:
|
:white_check_mark:
| |[populateEmptyPools()\*](../functions-n-handlers/utils/backfill.ts#populateemptypools)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| \* -> Different across chains --- ## PoolDayData Entity to store the daily metrics for each pool. ## Schema |Field|Type|derivedFrom|Description|Field Missing| |-|-|-|-|-| |id | ID! | | PoolDayData Entity ID. Format: `-` | |date | Int! | | Timestamp rounded to hour by dividing by 86400 | |pool | [Pool](./pool)! | | [Pool Entity](./pool) for which the daily metrics are tracked | |liquidity | BigInt! | | In range Liquidity at the end of the day | |sqrtPrice | BigInt! | | Pool Price at the end of the day | |token0Price | BigDecimal! | | Price of token0 in terms of token1 at the end of the day | |token1Price | BigDecimal! | | Price of token1 in terms of token0 at the end of the day | |tick | BigInt | | Actice Tick at the end of the day | |feeGrowthGlobal0X128 | BigInt! | | Global Fee Marker Value for token0 at the end of the day | arbitrum-one | |feeGrowthGlobal1X128 | BigInt! | | Global Fee Marker Value for token1 at the end of the day | arbitrum-one | |tvlUSD | BigDecimal! | | TVL available at the end of the day derived in USD | |volumeToken0 | BigDecimal! | | Total daily volume in token0 | |volumeToken1 | BigDecimal! | | Total daily volume in token1 | |volumeUSD | BigDecimal! | | Total daily volume in USD | |feesUSD | BigDecimal! | | Total swap fee taken during the day in terms of USD | |txCount | BigInt! | | No. of transactions in pool executed during the day | |open | BigDecimal! | | Open Price of token0 | |high | BigDecimal! | | High Price of token0 | |low | BigDecimal! | | Low Price of token0 | |close | BigDecimal! | | Close Price of token0 | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[updatePoolDayData()](../functions-n-handlers/utils/intervalUpdates.ts#updatepooldaydata)|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| --- ## PoolHourData Entity to store the hourly stats for each pool. ## Schema |Field|Type|derivedFrom|Description|Field Missing| |-|-|-|-|-| |id | ID! | | PoolHourData Entity ID. Format: `-` | |periodStartUnix | Int! | | Timestamp rounded to hour by dividing by 3600 | |pool | [Pool](./pool)! | | [Pool Entity](./pool) for which the hourly metrics are tracked | |liquidity | BigInt! | | In range Liquidity at the end of the hour | |sqrtPrice | BigInt! | | Pool Price at the end of the hour | |token0Price | BigDecimal! | | Price of token0 in terms of token1 at the end of the hour | |token1Price | BigDecimal! | | Price of token1 in terms of token0 at the end of the hour | |tick | BigInt | | Actice Tick at the end of the hour | |feeGrowthGlobal0X128 | BigInt! | | Global Fee Marker Value for token0 at the end of the hour | arbitrum-one | |feeGrowthGlobal1X128 | BigInt! | | Global Fee Marker Value for token1 at the end of the hour | arbitrum-one | |tvlUSD | BigDecimal! | | TVL available at the end of the hour derived in USD | |volumeToken0 | BigDecimal! | | Total hourly volume in token0 | |volumeToken1 | BigDecimal! | | Total hourly volume in token1 | |volumeUSD | BigDecimal! | | Total hourly volume in USD | |feesUSD | BigDecimal! | | Total swap fee taken during the hour in terms of USD | |txCount | BigInt! | | No. of transactions in pool executed during the hour | |open | BigDecimal! | | Open Price of token0 | |high | BigDecimal! | | High Price of token0 | |low | BigDecimal! | | Low Price of token0 | |close | BigDecimal! | | Close Price of token0 | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[updatePoolHourData()](../functions-n-handlers/utils/intervalUpdates.ts#updatehourdaydata)|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:*
| \* -> Different across chains --- ## Position Entity to store details of a position created through [NonfungiblePositionManager](../contracts/nonfungiblepositionmanager). Stores it's metadata, deposited/withdrawn tokens, fee variables and transactions where it participated. :::caution Entity Not Defined - Currently the entity is not defined for arbitrum-one chain ::: ## Schema |Field|Type|derivedFrom|Description|Field Missing| |-|-|-|-|-| |id | ID! | | Position NFT Token ID | |owner | Bytes! | | Position NFT owner's address | |pool | [Pool](./pool)! | | [Pool](./pool) where the position staked the tokens | |token0 | [Token](./token)! | | token0 entity of the pool | |token1 | [Token](./token)! | | token1 entity of the pool | |tickLower | [Tick](./tick)! | | Lower [Tick Entity](./tick) of the position | |tickUpper | [Tick](./tick)! | | Upper [Tick Entity](./tick) of the position | |liquidity | BigInt! | | Total liquidity added by the position | |depositedToken0 | BigDecimal! | | Total amount token0 ever deposited to the position | |depositedToken1 | BigDecimal! | | Total amount token1 ever deposited to the position | |withdrawnToken0 | BigDecimal! | | Total amount token0 withdrawn to the position (excluding fees) | |withdrawnToken1 | BigDecimal! | | Total amount token1 withdrawn to the position (excluding fees) | |collectedToken0 | BigDecimal! | | Total amount of token0 collected (inclusive of burn amounts) | mainnet | |collectedToken1 | BigDecimal! | | Total amount of token1 collected (inclusive of burn amounts) | mainnet | |collectedFeesToken0 | BigDecimal! | | Total amount token0 fee collected | |collectedFeesToken1 | BigDecimal! | | Total amount token1 fee collected | |amountDepositedUSD | BigDecimal! | | Total amount deposited in terms of USD | mainnet, optimsim | |amountWithdrawnUSD | BigDecimal! | | Total amount withdrawn in terms of USD | mainnet, optimism | |amountCollectedUSD | BigDecimal! | | Total amount collected in terms of USD | mainnet, optimism | |transaction | [Transaction](./transaction)! | | [Transaction entity](./transaction) in which the position was created | |feeGrowthInside0LastX128 | BigInt! | | Tracking the amount of token0 fee accumulated by the position | |feeGrowthInside1LastX128 | BigInt! | | Tracking the amount of token1 fee accumulated by the position | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[getPosition()](../functions-n-handlers/mappings/position-manager.ts#getposition)|
:white_check_mark:
||
:white_check_mark:
|| |[updateFeeVars()](../functions-n-handlers/mappings/position-manager.ts#updatefeevars)|||
:white_check_mark:
|| |[savePositionSnapshot()](../functions-n-handlers/mappings/position-manager.ts#savepositionsnapshot)||
:white_check_mark:
||| |[handleIncreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handleincreaseliquidity)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleDecreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handledecreaseliquidity)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleCollect()](../functions-n-handlers/mappings/position-manager.ts#handlecollect)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleTransfer()](../functions-n-handlers/mappings/position-manager.ts#handletransfer)|||
:white_check_mark:
|
:white_check_mark:
| --- ## PositionSnapshot Entity storing the state of a position after an action taken on the position. :::caution Entity Not Defined - Currently the entity is not defined for arbitrum-one chain ::: ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Position Snapshot ID. Format: `#` | |owner | Bytes! | | Position NFT's Owner's Address | |pool | [Pool](./pool)! | | [Pool](./pool) entity to which the position belongs to | |position | [Position](./position)! | | [Position](./position) entity for which the snapshot is taken | |blockNumber | BigInt! | | BlockNumber in which the snapshot was created | |timestamp | BigInt! | | Timestamp of the block in which the snapshot was created | |liquidity | BigInt! | | Total Liquidity of the position | |depositedToken0 | BigDecimal! | | Total amount of token0 ever deposited to the position | |depositedToken1 | BigDecimal! | | Total amount of token1 ever deposited to the position | |withdrawnToken0 | BigDecimal! | | Total amount of token0 withdrawn from the position (excluding the fee) | |withdrawnToken1 | BigDecimal! | | Total amount of token1 withdrawn from the position (excluding the fee) | |collectedFeesToken0 | BigDecimal! | | Total amount of token0 fee collected | |collectedFeesToken1 | BigDecimal! | | Total amount of token1 fee collected | |transaction | [Transaction](./transaction)! | | [Transaction](./transaction) in which the Snapshot was created. | |feeGrowthInside0LastX128 | BigInt! | | Marker to compute the position's token0 fee in a pool | |feeGrowthInside1LastX128 | BigInt! | | Marker to compute the position's token1 fee in a pool | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[savePositionSnapshot()](../functions-n-handlers/mappings/position-manager.ts#savepositionsnapshot)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| --- ## Swap Entity to stores details of a swap event emitted while swapping one token for the other in a pool. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Swap Entity ID. Format: `#` | |transaction | [Transaction](./transaction)! | | [Transaction Entity](./transaction) in which the swap event was emitted | |timestamp | BigInt! | | Timestamp of the block in which which the swap event was emitted | |pool | [Pool](./pool)! | | [Pool](./pool) in which the swap event was emitted | |token0 | [Token](./token)! | | token0 entity of the pool | |token1 | [Token](./token)! | | token1 entity of the pool | |sender | Bytes | | the address that triggered the swap | |recipient | Bytes! | | The address that gets their tokens swapped | |origin | Bytes! | | The EOA address that initiated the transaction | |amount0 | BigDecimal! | | Amount of token0 swapped | |amount1 | BigDecimal! | | Amount of token1 swapped | |amountUSD | BigDecimal | | Swapped token value derived in USD based on available prices of tokens | |sqrtPriceX96 | BigInt! | | The sqrt(price) of the pool after the swap, as a Q64.96 | |tick | BigInt! | | The tick after the swap | |logIndex | BigInt | | Order of the swap event within the logs of the transaction | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| --- ## Tick Entity to stores the metadata for a tick in a pool, it's lifetime metrics and current liquidity and fee variables. ## Schema |Field|Type|derivedFrom|Description|Field Missing| |-|-|-|-|-| |id | ID! | | Tick ID. Format: `#`| |poolAddress | String | | [Pool](./pool) contract address | |tickIdx | BigInt! | | [Tick](./tick) Index | |pool | [Pool](./pool)! | | [Pool](./pool) Entity | |liquidityGross | BigInt! | | Total Liquidity the pool has at the tick with it's lower and upper range. | |liquidityNet | BigInt! | | Liquidity change when the tick is crossed | |price0 | BigDecimal! | | Price of token0 at this tick (constant) | |price1 | BigDecimal! | | Price of token1 at this tick (constant) | |volumeToken0 | BigDecimal! | | Total volume of token0 transacted with tick in active range | |volumeToken1 | BigDecimal! | | Total volume of token1 transacted with tick in active range | |volumeUSD | BigDecimal! | | Total transacted value with tick in active range in USD | |untrackedVolumeUSD | BigDecimal! | | Total transacted value with tick in active range (including tokens with unreliable USD value) in USD | |feesUSD | BigDecimal! | | Fee collected with the tick in active range in USD | |collectedFeesToken0 | BigDecimal! | | Total amount of token0 collected as fee when this tick is active | |collectedFeesToken1 | BigDecimal! | | Total amount of token1 collected as fee when this tick is active | |collectedFeesUSD | BigDecimal! | | Total value of fee collected when this tick is active is USD | |createdAtTimestamp | BigInt! | | BlockTime when the tick was initialized | |createdAtBlockNumber | BigInt! | | BlockNumber when the tick was initialized | |liquidityProviderCount | BigInt! | | Used for detecting new exchanges. (Not Used currently) | |feeGrowthOutside0X128 | BigInt! | | Used for calculating token0 fee's accumulated outside a tick whenever it is crossed | arbitrum-one | |feeGrowthOutside1X128 | BigInt! | | Used for calculating token1 fee's accumulated outside a tick whenever it is crossed | arbitrum-one | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[createTick()](../functions-n-handlers/utils/tick.ts#createtick)|
:white_check_mark:
||
:white_check_mark:
|| |[updateTickDayData()](../functions-n-handlers/utils/intervalUpdates.ts#updatetickdaydata)||
:white_check_mark:
||| |[handleMint()](../functions-n-handlers/mappings/core.ts#handlemint)|||
:white_check_mark:
|| |[handleBurn()](../functions-n-handlers/mappings/core.ts#handleburn)|||
:white_check_mark:
|| |[updateTickFeeVarsAndSave()](../functions-n-handlers/mappings/core.ts#updatetickfeevarsandsave)|||
:white_check_mark:
|
:white_check_mark:
| |[loadTickUpdateFeeVarsAndSave()](../functions-n-handlers/mappings/core.ts#loadtickupdatefeevarsandsave)||
:white_check_mark:
||| --- ## TickDayData Entities storing the details of Liquidity available & volume of token traded at a tick on a given day. :::info An entity is created only if there is a change during the day ::: ## Schema |Field|Type|derivedFrom|Description|Field Missing| |-|-|-|-|-| |id | ID! | | TickDayData Entity ID. Format: `--` | |date | Int! | | Timestamp rounded to the day by dividing by 86400 | |pool | [Pool](./pool)! | | [Pool Entity](./pool) for which the daily tick metrics were recorded | |tick | [Tick](./tick)! | | [Tick Entity](./tick) for which daily metrics were recorded | |liquidityGross | BigInt! | | Total liquidity around the tick (1-tick range below or above) at end of the day | |liquidityNet | BigInt! | | Change in liquidity when the tick is crossed at end of the day | |volumeToken0 | BigDecimal! | | Daily volume of token0 with this tick in active range | |volumeToken1 | BigDecimal! | | Daily volume of token1 with this tick in active range | |volumeUSD | BigDecimal! | | Daily swap value with this tick in active range in derived USD | |feesUSD | BigDecimal! | | Daily swap fee with this tick in active range in derived USD | |feeGrowthOutside0X128 | BigInt! | | token0 fee accumulated marker outside the tick range at the end of the day | arbitrum-one | |feeGrowthOutside1X128 | BigInt! | | token1 fee accumulated marker outside the tick range at the end of the day | arbitrum-one | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[updateTickDayData()](../functions-n-handlers/utils/intervalUpdates.ts#updatetickdaydata)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| --- ## TickHourData Entities capturing details of Liquidity available & Volume of token traded at a tick for a given hour. :::info Not Used The Schema is not currently populated in mainnet subgraph ::: ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | TickHourData Entity ID. Format: `--` | |periodStartUnix | Int! | | Timestamp rounded to the hour by dividing by 3600 | |pool | [Pool](./pool)! | | [Pool Entity](./pool) for which the hourly tick metrics were recorded | |tick | [Tick](./tick)! | | [Tick Entity](./tick) for which hourly metrics were recorded | |liquidityGross | BigInt! | | Total liquidity around the tick (1-tick range below or above) at end of the hour | |liquidityNet | BigInt! | | Change in liquidity when the tick is crossed at end of the hour | |volumeToken0 | BigDecimal! | | Hourly volume of token0 with this tick in active range | |volumeToken1 | BigDecimal! | | Hourly volume of token1 with this tick in active range | |volumeUSD | BigDecimal! | | Hourly swap value with this tick in active range in derived USD | |feesUSD | BigDecimal! | | Hourly swap fee with this tick in active range in derived USD | --- ## Token Entity to stores the metadata and token level metrics for a token present in any of the pools. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Token Contract Address | |symbol | String! | | Token Symbol | |name | String! | | Token Name | |decimals | BigInt! | | No. of decimals in the token value | |totalSupply | BigInt! | | Total supply of the token | |volume | BigDecimal! | | Total token volume traded in swaps | |volumeUSD | BigDecimal! | | Total token value traded in swaps in USD | |untrackedVolumeUSD | BigDecimal! | | Total token value traded in USD, including pools with unreliable USD values | |feesUSD | BigDecimal! | | Amount of Fees taken from token swaps in derived in USD | |txCount | BigInt! | | No. of transactions across all pools that include this token | |poolCount | BigInt! | | No. of pools containing this token | |totalValueLocked | BigDecimal! | | Liquidity across all pools for the token | |totalValueLockedUSD | BigDecimal! | | Liquidity across all pools for the token in terms of USD value | |totalValueLockedUSDUntracked | BigDecimal! | | Liquidity across all pools (including pools with unreliable USD values) for the token in terms of USD value | |derivedETH | BigDecimal! | | Price of token relative to ETH | |whitelistPools | [[Pool](./pool)!]! | | [Pool](./pool) entities which can can be used for reliable USD pricing of the token | |tokenDayData | [[TokenDayData](./tokendaydata)!]! | @derivedFrom(field: "token") | Link to daily stats for the token | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[sqrtPriceX96ToTokenPrices()](../functions-n-handlers/utils/pricing.ts#sqrtpricex96totokenprices)||
:white_check_mark:
||| |[findEthPerToken()](../functions-n-handlers/utils/pricing.ts#findethpertoken)||
:white_check_mark:
||| |[getTrackedAmountUSD()](../functions-n-handlers/utils/pricing.ts#gettrackedamountusd)||
:white_check_mark:
||| |[updateTokenDayData()](../functions-n-handlers/utils/intervalUpdates.ts#updatetokendaydata)||
:white_check_mark:
||| |[updateTokenHourData()](../functions-n-handlers/utils/intervalUpdates.ts#updatetokenhourdata)||
:white_check_mark:
||| |[handleInitialize()](../functions-n-handlers/mappings/core.ts#handleinitialize)||
:white_check_mark:*
|
:white_check_mark:
|
:white_check_mark:
| |[handleMint()](../functions-n-handlers/mappings/core.ts#handlemint)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleBurn()](../functions-n-handlers/mappings/core.ts#handleburn)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:*
| |[handlePoolCreated()](../functions-n-handlers/mappings/factory.ts#handlepoolcreated)|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| |[handleIncreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handleincreaseliquidity)||
:white_check_mark:
||| |[handleDecreaseLiquidity()](../functions-n-handlers/mappings/position-manager.ts#handledecreaseliquidity)||
:white_check_mark:
||| |[handleCollect()](../functions-n-handlers/mappings/position-manager.ts#handlecollect)||
:white_check_mark:
||| |[populateToken()\*](../functions-n-handlers/utils/backfill.ts#populatetoken)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| |[populateEmptyPools()\*](../functions-n-handlers/utils/backfill.ts#populateemptypools)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| \* -> Different across chains --- ## TokenDayData Entities capturing the daily stats for a token across all of Uniswap. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | TokenDayData Entity ID. Format: `-` | |date | Int! | | Timestamp rounded to the day by dividing by 86400 | |token | [Token](./token)! | | [Token Entity](./token) for which the daily metric were recorded | |volume | BigDecimal! | | Daily swap volume of the token | |volumeUSD | BigDecimal! | | Daily swap volume of the token in derived USD | |untrackedVolumeUSD | BigDecimal! | | Daily swap volume of the token in derived USD (including in pools with tokens with unreliable USD value) | |totalValueLocked | BigDecimal! | | Liquidity across all pools in token units at the end of the day | |totalValueLockedUSD | BigDecimal! | | Total value of liquidity across all pools in token units in derived USD at the end of the day| |priceUSD | BigDecimal! | | Price of token in USD at the end of the day | |feesUSD | BigDecimal! | | Total Fee Collected in derived USD at the end of the day | |open | BigDecimal! | | Open Price of the token | |high | BigDecimal! | | High Price of the token | |low | BigDecimal! | | Low Price of the token | |close | BigDecimal! | | Close Price of the token | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[updateTokenDayData()](../functions-n-handlers/utils/intervalUpdates.ts#updatetokendaydata)|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| --- ## TokenHourData Entities capturing the hourly metrics for a token across all of Uniswap. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | TokenHourData Entity ID. Format: `-` | |periodStartUnix | Int! | | Timestamp rounded to the hour by dividing by 3600 | |token | [Token](./token)! | | [Token Entity](./token) for which the hourly metric were recorded | |volume | BigDecimal! | | Hourly swap volume of the token | |volumeUSD | BigDecimal! | | Hourly swap volume of the token in derived USD | |untrackedVolumeUSD | BigDecimal! | | Hourly swap volume of the token in derived USD (including in pools with tokens with unreliable USD value) | |totalValueLocked | BigDecimal! | | Liquidity across all pools in token units at the end of the hour | |totalValueLockedUSD | BigDecimal! | | Total value of liquidity across all pools in token units in derived USD at the end of the hour| |priceUSD | BigDecimal! | | Price of token in USD at the end of the hour | |feesUSD | BigDecimal! | | Total Fee Collected in derived USD at the end of the hour | |open | BigDecimal! | | Open Price of the token | |high | BigDecimal! | | High Price of the token | |low | BigDecimal! | | Low Price of the token | |close | BigDecimal! | | Close Price of the token | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[updateTokenHourData()](../functions-n-handlers/utils/intervalUpdates.ts#updatetokenhourdata)|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| --- ## Transaction Entity capturing uniswap transaction details with a list of mint, burn, swap, flash and collects events emitted within a transaction. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Transaction Hash | |blockNumber | BigInt! | | Block Number where the transaction was added to the chain | |timestamp | BigInt! | | Timestamp of the block where the transaction was added to the chain | |gasUsed | BigInt! | | Amount of Gas Units Consumed to execute the transaction | |gasPrice | BigInt! | | Cost of one unit Gas paid for the transaction | |mints | [[Mint](./mint)]! | @derivedFrom(field: "transaction") | [Mint](./mint) entities created in this transaction | |burns | [[Burn](./burn)]! | @derivedFrom(field: "transaction") | [Burn](./burn) entities created in this transaction | |swaps | [[Swap](./swap)]! | @derivedFrom(field: "transaction") | [Swap](./swap) entities created in this transaction | |flashed | [[Flash](./flash)]! | @derivedFrom(field: "transaction") | [Flash](./flash) entities created in this transaction | |collects | [[Collect](./collect)]! | @derivedFrom(field: "transaction") | [Collect](./collect) entities created in this transaction | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[loadTransaction()](../functions-n-handlers/utils/index.ts#loadtransaction)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| |[handleMint()](../functions-n-handlers/mappings/core.ts#handlemint)||
:white_check_mark:
||| |[handleBurn()](../functions-n-handlers/mappings/core.ts#handleburn)||
:white_check_mark:
||| |[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)||
:white_check_mark:
||| |[getPosition()](../functions-n-handlers/mappings/position-manager.ts#getposition)||
:white_check_mark:
||| |[savePositionSnapshot()](../functions-n-handlers/mappings/position-manager.ts#savepositionsnapshot)||
:white_check_mark:
||| --- ## UniswapDayData Entities capturing the daily metrics for all of the Uniswap protocol. ## Schema |Field|Type|derivedFrom|Description| |-|-|-|-| |id | ID! | | Timestamp rounded to current day by dividing by 86400 | |date | Int! | | Timestamp rounded to current day by dividing by 86400 | |volumeETH | BigDecimal! | | Total daily volume in Uniswap derived in terms of ETH | |volumeUSD | BigDecimal! | | Total daily volume in Uniswap derived in terms of USD | |volumeUSDUntracked | BigDecimal! | | total daily volume in Uniswap derived in terms of USD (including tokens with unreliable USD value) | |feesUSD | BigDecimal! | | Amount of swap fee taken during the day in terms of USD | |txCount | BigInt! | | No. of transactions that occurred during the day | |tvlUSD | BigDecimal! | | TVL locked at the end of the day in terms of USD | ## Referencing Functions |FunctionName|Create|Read|Update|Save| |-|-|-|-|-| |[updateUniswapDayData()](../functions-n-handlers/utils/intervalUpdates.ts#updateuniswapdaydata)|
:white_check_mark:
||
:white_check_mark:
|
:white_check_mark:
| |[handleSwap()](../functions-n-handlers/mappings/core.ts#handleswap)||
:white_check_mark:
|
:white_check_mark:
|
:white_check_mark:
| --- ## Subgraph Configuration ### Subgraphs :::info The subgraphs link are picked from the clients specified in [v3-info](https://github.com/Uniswap/v3-info/blob/master/src/apollo/client.ts) ::: |Subgraph|Code Branch Referred|Graft Base|Graft Block|NFT Position Manager Address|NFT Position Manager Start Block| |-|-|-|-|-|-| |[Mainnet](https://thegraph.com/hosted-service/subgraph/uniswap/uniswap-v3)|[main](https://github.com/Uniswap/v3-subgraph/tree/main)|QmS13421u6qsbVdBCrZhdRaZw2wH67drwF3urmueJvvJ5P|13591197|0xC36442b4a4522E871399CD717aBDD847Ab11FE88|12369651| |[Polygon](https://thegraph.com/hosted-service/subgraph/ianlapham/uniswap-v3-polygon)|[polygon](https://github.com/Uniswap/v3-subgraph/tree/polygon)|QmUKRxaVLRpn18UA9mwukAwRQudH9TfBW5wiKCWEJghUS8|25459720|0xC36442b4a4522E871399CD717aBDD847Ab11FE88|22760586| |[Arbitrum-One](https://thegraph.com/hosted-service/subgraph/ianlapham/uniswap-arbitrum-one)|[arbitrum-minimal](https://github.com/Uniswap/v3-subgraph/tree/arbitrum-minimal)|None|None|None|None| |[Optimism](https://thegraph.com/hosted-service/subgraph/ianlapham/optimism-post-regenesis)|[ian/optimism-fix](https://github.com/Uniswap/v3-subgraph/tree/ian/optimism-fix)|Qmf9i13TJknQwcamcLbb75hEhbf3nqDi321XWhtFBj3P5s|10028767|0xC36442b4a4522E871399CD717aBDD847Ab11FE88|0| --- ## Integration Guide # Trading API Integration Guide This guide provides comprehensive documentation for integrating with the Uniswap Trading API, including schema definitions, validation requirements, error handling, and best practices. ## Table of Contents - [API Endpoints](#api-endpoints) - [Schema Reference](#schema-reference) - [Permit2 Flow](#permit2-flow) - [Error Handling](#error-handling) - [Best Practices](#best-practices) - [Troubleshooting](#troubleshooting) ## API Endpoints ### POST /quote Generate a quote for a token swap. **Request Body:** ```typescript interface QuoteRequest { // Required tokenIn: string; // Token address to swap from tokenOut: string; // Token address to swap to tokenInChainId: number; // Chain ID for input token tokenOutChainId: number; // Chain ID for output token type: 'EXACT_INPUT' | 'EXACT_OUTPUT'; amount: string; // Amount in token's smallest unit (wei) swapper: string; // User's wallet address // Slippage (choose one, mutually exclusive) slippageTolerance?: number; // Manual slippage as percentage (e.g., 0.5 = 0.5%) autoSlippage?: 'DEFAULT'; // Automatic slippage calculation // Optional protocols?: Protocol[]; // Limit to specific protocols routingPreference?: RoutingPreference; urgency?: 'urgent' | 'normal'; permitAmount?: 'FULL' | 'EXACT'; gasStrategies?: GasStrategy[]; } ``` **Response:** ```typescript interface QuoteResponse { requestId: string; routing: SwapType; // See SwapType enum // Quote (one of, depends on routing type) classicQuote?: ClassicQuote; bridgeQuote?: BridgeQuote; dutchLimitQuote?: DutchLimitQuote; dutchLimitV2Quote?: DutchLimitV2Quote; dutchLimitV3Quote?: DutchLimitV3Quote; wrapUnwrapQuote?: WrapUnwrapQuote; priorityQuote?: PriorityQuote; chainedQuote?: ChainedQuote; // Permit data (if permit requested) permitSingleData?: PermitSingleData; permitTransferFromData?: PermitTransferFromData; permitTransaction?: TransactionRequest; permitGasFee?: string; } ``` ### POST /swap Convert a quote into an unsigned transaction ready for signing. **Request Body:** ```typescript interface SwapRequest { // Quote (required) quote: ClassicQuote | WrapUnwrapQuote | BridgeQuote; // Permit2 signature (optional, but both required if either present) signature?: string; // EIP-712 signature for Permit2 permitData?: PermitSingleData; // Required if signature provided // Optional parameters safetyMode?: 'RELAXED' | 'SAFE'; deadline?: number; // Unix timestamp simulateTransaction?: boolean; refreshGasPrice?: boolean; urgency?: 'urgent' | 'normal'; gasStrategies?: GasStrategy[]; } ``` :::warning Important The `signature` and `permitData` fields must either both be present or both be omitted. See [Permit2 Flow](#permit2-flow) for details. ::: **Response:** ```typescript interface SwapResponse { requestId: string; swap: TransactionRequest; // Transaction to sign and broadcast gasFee?: string; gasEstimates?: GasEstimate[]; txFailureReasons?: TransactionFailureReason[]; } ``` ### POST /check_approval Check if token approval is required before swapping. **Request Body:** ```typescript interface CheckApprovalRequest { walletAddress: string; token: string; amount: string; chainId: number; urgency?: 'urgent' | 'normal'; includeGasInfo?: boolean; tokenOut?: string; tokenOutChainId?: number; } ``` **Response:** ```typescript interface CheckApprovalResponse { requestId: string; approval: TransactionRequest; // Approval transaction (if needed) cancel: TransactionRequest; // Cancel approval transaction gasFee?: string; cancelGasFee?: string; gasEstimates?: GasEstimate[]; } ``` ### POST /swap_5792 Generate a batch of transactions for EIP-5792 wallet_sendCalls. **Request Body:** ```typescript interface Swap5792Request { classicQuote?: ClassicQuote; wrapUnwrapQuote?: WrapUnwrapQuote; bridgeQuote?: BridgeQuote; permitData?: PermitSingleData; deadline: number; // Unix timestamp (required) urgency?: 'urgent' | 'normal'; } ``` **Response:** ```typescript interface Swap5792Response { requestId: string; from: string; chainId: number; calls: TransactionRequest5792[]; gasFee?: string; gasEstimates?: GasEstimate[]; } interface TransactionRequest5792 { to: string; data: string; value: string; } ``` ### POST /swap_7702 Generate a transaction with EIP-7702 delegation. **Request Body:** ```typescript interface Swap7702Request { classicQuote?: ClassicQuote; wrapUnwrapQuote?: WrapUnwrapQuote; bridgeQuote?: BridgeQuote; smartContractDelegationAddress: string; // Required permitData?: PermitSingleData; deadline?: number; urgency?: 'urgent' | 'normal'; gasStrategies?: GasStrategy[]; } ``` ### Cross-Chain Plans For cross-chain swaps (where `tokenInChainId !== tokenOutChainId`): - **POST /plan** - Create a plan from a CHAINED quote - **GET /plan/:planId** - Check plan status - **PATCH /plan/:planId** - Update plan with transaction hashes/signatures ```typescript // Example: Create cross-chain plan const quoteResponse = await fetch('/quote', { method: 'POST', headers: { 'x-api-key': API_KEY, 'x-chained-actions-enabled': 'true', // Required header 'Content-Type': 'application/json' }, body: JSON.stringify({ tokenIn: '0x...', tokenInChainId: 1, // Ethereum tokenOut: '0x...', tokenOutChainId: 8453, // Base // ... other params }) }); ``` ## Schema Reference ### TransactionRequest The `TransactionRequest` object returned by `/swap` contains all fields needed to broadcast a transaction: ```typescript interface TransactionRequest { to: string; // Contract address to call from: string; // User's wallet address data: string; // Encoded function call (hex string) value: string; // Native token amount (wei) chainId: number; gasLimit?: string; maxFeePerGas?: string; maxPriorityFeePerGas?: string; gasPrice?: string; // Legacy gas price } ``` #### Critical Field: `data` The `data` field contains the encoded contract call and **must always be present** in swap transactions. :::danger Validation Requirements 1. **Never Empty**: The `data` field must be a non-empty hex string (not `""` or `"0x"`) 2. **Always Validate**: Check `data` exists before broadcasting 3. **Revert Prevention**: Empty `data` causes on-chain transaction reverts ::: **Validation Example:** ```typescript function validateTransaction(tx: TransactionRequest): void { // Critical validation if (!tx.data || tx.data === '' || tx.data === '0x') { throw new Error('Transaction data is empty - invalid swap transaction'); } if (!tx.to || !tx.from) { throw new Error('Missing required transaction fields'); } // Verify valid hex if (!/^0x[0-9a-fA-F]*$/.test(tx.data)) { throw new Error('Transaction data is not valid hex'); } } // Use before broadcasting const swapResponse = await fetch('/swap', {...}); const {swap} = await swapResponse.json(); validateTransaction(swap); // Validate before signing const signedTx = await wallet.signTransaction(swap); const txHash = await provider.sendTransaction(signedTx); ``` ### PermitSingleData For Permit2-based approvals: ```typescript interface PermitSingleData { domain: TypedDataDomain; values: PermitSingle; types: Record; } interface PermitSingle { details: { token: string; amount: string; expiration: string; nonce: string; }; spender: string; sigDeadline: string; } ``` ## Permit2 Flow Permit2 enables gasless token approvals via EIP-712 signatures. ### When to Use Permit2 Use Permit2 when: - Swapping ERC-20 tokens (not native tokens) - User hasn't approved the token for Permit2 - You want to minimize transaction count (1 tx instead of 2) ### Implementation Steps #### 1. Get Quote with Permit Data ```typescript const quoteResponse = await fetch('/quote', { method: 'POST', headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tokenIn: '0x...', tokenOut: '0x...', amount: '1000000', type: 'EXACT_INPUT', swapper: '0x...', tokenInChainId: 1, tokenOutChainId: 1, permitAmount: 'EXACT', // or 'FULL' slippageTolerance: 0.5 }) }); const {quote} = await quoteResponse.json(); ``` #### 2. Sign the Permit ```typescript let signature: string | undefined; let permitData: PermitSingleData | undefined; if (quote.permitData) { signature = await wallet._signTypedData( quote.permitData.domain, quote.permitData.types, quote.permitData.values ); permitData = quote.permitData; } ``` #### 3. Submit to /swap :::warning Critical Field Requirements When including a Permit2 signature, both `signature` AND `permitData` must be provided: ::: ```typescript // CORRECT - Both fields provided const swapRequest = { classicQuote: quote, signature: signature, permitData: permitData }; // WRONG - Missing permitData const swapRequest = { classicQuote: quote, signature: signature // Will fail validation }; // WRONG - Null permitData const swapRequest = { classicQuote: quote, signature: signature, permitData: null // API rejects null, omit field instead }; // CORRECT - No permit (both fields omitted) const swapRequest = { classicQuote: quote // signature and permitData omitted entirely }; ``` **Field Omission Rules:** 1. **If using Permit2**: Include both `signature` and `permitData` 2. **If NOT using Permit2**: Omit both fields entirely (don't set to `null`) 3. **Never mix**: Don't provide one without the other #### 4. Broadcast Transaction ```typescript const {swap} = await swapResponse.json(); validateTransaction(swap); const signedTx = await wallet.signTransaction(swap); const txReceipt = await provider.sendTransaction(signedTx); ``` ### Permit2 Error Messages | Error | Cause | Solution | |-------|-------|----------| | `"permitData" must be of type object` | Field set to `null` | Omit field entirely | | `signature provided without permitData` | Missing `permitData` | Include both or neither | | `Invalid permit signature` | Wrong data signed or expired | Re-request quote and sign fresh | ## Error Handling ### HTTP Status Codes | Code | Meaning | |------|---------| | 200 | Request succeeded | | 400 | Invalid request (validation error) | | 401 | Invalid API key | | 429 | Rate limit exceeded | | 500 | API error (retry with backoff) | | 503 | Temporary unavailability (retry) | ### Error Response Format ```typescript interface ErrorResponse { error: string; message: string; details?: Record; } ``` ### Common Errors #### NO_QUOTES_AVAILABLE **Cause**: No route found for the requested swap **Solutions**: - Verify token addresses are correct and on the specified chain - Check sufficient liquidity exists for the trade size - Try different protocols or routing preferences - Reduce trade size #### INSUFFICIENT_RESERVES **Cause**: Liquidity insufficient for the requested amount **Solutions**: - Reduce trade amount - Split into multiple smaller trades - Try alternative routing #### VALIDATION_ERROR **Cause**: Invalid request parameters **Solutions**: - Check all required fields are present - Verify addresses are valid checksummed addresses - Ensure amount is in correct units (wei, not ether) - Validate chain IDs are supported ### Transaction Revert Scenarios If a transaction reverts on-chain: 1. **Check `data` field**: Verify it's not empty 2. **Verify token balance**: User has sufficient tokens at broadcast time 3. **Check allowance**: Token approval is sufficient (if not using Permit2) 4. **Check slippage**: Price moved beyond slippage tolerance 5. **Check deadline**: Quote expired before broadcast 6. **Nonce collision**: Another transaction used the same nonce **Recommended Client-Side Checks:** ```typescript async function validateBeforeBroadcast( tx: TransactionRequest, provider: Provider, token: string, amount: string ): Promise { // 1. Validate transaction structure if (!tx.data || tx.data === '' || tx.data === '0x') { throw new Error('Invalid transaction: empty data field'); } // 2. Check native balance const balance = await provider.getBalance(tx.from); if (balance.lt(tx.value)) { throw new Error('Insufficient native token balance'); } // 3. Check ERC-20 balance (if applicable) if (token !== NATIVE_TOKEN_ADDRESS) { const tokenContract = new Contract(token, ERC20_ABI, provider); const tokenBalance = await tokenContract.balanceOf(tx.from); if (tokenBalance.lt(amount)) { throw new Error('Insufficient token balance'); } } // 4. Simulate transaction (optional but recommended) try { await provider.call(tx); } catch (error) { throw new Error(`Transaction simulation failed: ${error.message}`); } } ``` ## Best Practices ### 1. Quote Freshness Quotes are time-sensitive due to price volatility: - **Refresh quotes** if more than 30 seconds old before broadcasting - **Use `deadline`** parameter to prevent execution of stale quotes - **Monitor price impact** and warn users of significant changes ```typescript const QUOTE_EXPIRY_MS = 30000; // 30 seconds const quoteTimestamp = Date.now(); // ... user reviews and signs ... if (Date.now() - quoteTimestamp > QUOTE_EXPIRY_MS) { quote = await fetchQuote(params); // Fetch fresh quote } ``` ### 2. Transaction Validation Always validate transaction payloads before broadcasting: ```typescript function validateSwapTransaction(tx: TransactionRequest): void { if (!tx.data || tx.data === '' || tx.data === '0x') { throw new Error('Transaction data is empty'); } if (!tx.to || !isAddress(tx.to)) { throw new Error('Invalid recipient address'); } if (!tx.from || !isAddress(tx.from)) { throw new Error('Invalid sender address'); } if (tx.maxFeePerGas && tx.gasPrice) { throw new Error('Cannot set both maxFeePerGas and gasPrice'); } if (tx.value && BigNumber.from(tx.value).lt(0)) { throw new Error('Invalid transaction value'); } } ``` ### 3. Gas Management The API provides gas estimates, but clients should: - **Apply gas buffer**: Add 10-20% to estimated gas limit - **Update gas prices**: Use `refreshGasPrice: true` for fresh estimates - **Handle gas spikes**: Warn users when gas is unusually high - **EIP-1559 vs Legacy**: Use EIP-1559 on supported chains ```typescript function applyGasBuffer(tx: TransactionRequest): TransactionRequest { if (tx.gasLimit) { const buffered = BigNumber.from(tx.gasLimit) .mul(120) // 120% .div(100); return {...tx, gasLimit: buffered.toString()}; } return tx; } ``` ### 4. Slippage Configuration Balance protection vs execution success: | Setting | Slippage | Use Case | |---------|----------|----------| | Conservative | 0.1-0.5% | Stable pairs, low volatility | | Moderate | 0.5-1% | Most swaps | | Aggressive | 1-5% | Large trades, volatile markets, low liquidity | ### 5. Error Recovery Implement retry logic with exponential backoff: ```typescript async function fetchQuoteWithRetry( params: QuoteRequest, maxRetries = 3 ): Promise { for (let attempt = 0; attempt < maxRetries; attempt++) { try { const response = await fetch('/quote', { method: 'POST', headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify(params) }); if (!response.ok) { if (response.status === 429) { await sleep(Math.pow(2, attempt) * 1000); continue; } throw new Error(`API error: ${response.status}`); } return await response.json(); } catch (error) { if (attempt === maxRetries - 1) throw error; await sleep(Math.pow(2, attempt) * 1000); } } throw new Error('Max retries exceeded'); } ``` ### 6. Monitoring & Logging Track key metrics for production reliability: ```typescript interface SwapMetrics { quoteLatency: number; swapLatency: number; quoteFreshness: number; gasEstimateAccuracy: number; revertRate: number; } function logSwapAttempt( params: QuoteRequest, quote: QuoteResponse, txHash?: string, error?: Error ): void { const metrics = { timestamp: Date.now(), chainId: params.tokenInChainId, tokenIn: params.tokenIn, tokenOut: params.tokenOut, amount: params.amount, routing: quote.routing, txHash, success: !!txHash && !error, error: error?.message }; analytics.track('swap_attempt', metrics); } ``` ## Troubleshooting ### Quote Issues **Problem**: No quotes returned **Checklist**: - [ ] Token addresses are valid for the specified chains - [ ] Liquidity exists for the trading pair - [ ] Amount is within reasonable bounds - [ ] Chain IDs are supported by the API - [ ] Protocols specified (if any) are available **Problem**: Quote price seems incorrect **Checklist**: - [ ] Amount is in correct units (wei, not ether) - [ ] Token decimals are correct - [ ] Slippage tolerance is appropriate - [ ] Price impact is acceptable for trade size ### Transaction Issues **Problem**: Transaction reverts on-chain **Checklist**: - [ ] `data` field is not empty - [ ] Token balance sufficient at broadcast time - [ ] Token approval sufficient (if not using Permit2) - [ ] Quote not expired (deadline not passed) - [ ] Slippage tolerance not too tight - [ ] Gas limit sufficient - [ ] Nonce correct (no collisions) **Problem**: Transaction takes too long to confirm **Checklist**: - [ ] Gas price competitive (use `refreshGasPrice: true`) - [ ] Network not congested - [ ] Transaction not stuck (check nonce) ### API Issues **Problem**: 429 Rate Limit Exceeded **Solution**: Implement exponential backoff and request caching: ```typescript const cache = new Map(); async function fetchWithCache( url: string, params: any, cacheDuration = 30000 ): Promise { const cacheKey = `${url}:${JSON.stringify(params)}`; const cached = cache.get(cacheKey); if (cached && Date.now() - cached.timestamp < cacheDuration) { return cached.data; } const data = await fetchWithRetry(url, params); cache.set(cacheKey, {data, timestamp: Date.now()}); return data; } ``` **Problem**: Inconsistent responses **Solution**: Include `requestId` in all related requests for debugging: ```typescript const requestId = generateUUID(); const quote = await fetchQuote({...params, requestId}); const swap = await fetchSwap({classicQuote: quote, requestId}); ``` ### Integration Issues **Problem**: Empty `data` field in transaction **Solution**: 1. Add validation before broadcasting 2. Log the full response for debugging 3. Contact support with `requestId` if persistent ```typescript const {swap} = await swapResponse.json(); if (!swap.data || swap.data === '' || swap.data === '0x') { console.error('Invalid swap response:', { requestId: swap.requestId, swap }); throw new Error('Empty transaction data - please contact support'); } ``` **Problem**: Permit2 validation errors **Solution**: Follow exact field requirements: ```typescript const swapRequest: SwapRequest = { classicQuote: quote }; // Only add permit fields if both are present if (signature && permitData) { swapRequest.signature = signature; swapRequest.permitData = permitData; } // Otherwise omit entirely (don't set to null) ``` ## Limitations - **UniswapX V2**: Mainnet, Arbitrum, Base, Unichain only - **UniswapX V3**: Arbitrum only - **L2 UniswapX minimum**: 300 USDC equivalent - **Native token swaps**: No UniswapX for native token input or wrapping ## Support For additional support: - **Discord**: Join the Uniswap developer community - **GitHub Issues**: Report bugs and issues When reporting issues, include: - Request ID from API response - Full request/response payloads (sanitize sensitive data) - Chain ID and transaction hash (if applicable) - Timestamp of the request --- ## Overview(Trading) # Trading API The Uniswap Trading API provides quote generation and transaction building for token swaps across 25+ chains. This API handles route optimization, gas estimation, and transaction encoding - you handle balance checks, transaction signing, and broadcasting. ## Quick Start ### Authentication All requests require an API key: ```bash curl -X POST https://trade.api.uniswap.org/v1/quote \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"tokenIn":"0x...","tokenOut":"0x...","amount":"1000000",...}' ``` ### Basic Quote Request ```typescript const response = await fetch('https://trade.api.uniswap.org/v1/quote', { method: 'POST', headers: { 'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ tokenIn: '0x0000000000000000000000000000000000000000', // ETH tokenOut: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT tokenInChainId: 1, tokenOutChainId: 1, type: 'EXACT_INPUT', amount: '1000000000000000000', // 1 ETH in wei swapper: '0x...', // User's wallet address slippageTolerance: 0.5 // 0.5% }) }); const quote = await response.json(); ``` ## Architecture ### Client-Side Responsibilities The Trading API is a quote and transaction building service. Your application handles: 1. **Balance Checks**: Verify token balances before requesting quotes 2. **Allowance Management**: Check and request token approvals (ERC-20 `approve` or Permit2) 3. **Nonce Management**: Track transaction nonces for the user's wallet 4. **Gas Estimation**: Verify gas estimates before broadcasting 5. **Transaction Broadcasting**: Sign and submit transactions via your RPC provider 6. **Transaction Monitoring**: Track confirmations and handle reverts ### Required Infrastructure Your integration must include: - **RPC Provider**: Connection to blockchain nodes (Infura, Alchemy, or self-hosted) - **Web3 Library**: ethers.js, viem, or web3.js for transaction signing - **Wallet Integration**: WalletConnect, MetaMask, or similar for user signing ### Data Flow ``` User Request | Your Application |-- Check balances (via your RPC) |-- Request quote (Trading API) |-- Build transaction (Trading API response) |-- Check allowances (via your RPC) |-- Get user signature (Wallet) |-- Manage nonce (your tracking) +-- Broadcast transaction (via your RPC) | Blockchain ``` ## Available Endpoints | Endpoint | Description | |----------|-------------| | [POST /quote](./quote) | Generate a quote for a token swap | | [POST /swap](./swap) | Convert a quote into an unsigned transaction | | [POST /check_approval](./check-approval) | Check if token approval is required | | [POST /swap_5792](./swap-5792) | Generate batch transactions for EIP-5792 | | [POST /swap_7702](./swap-7702) | Generate transaction with EIP-7702 delegation | | [Cross-Chain Plans](./cross-chain) | Multi-step cross-chain swap endpoints | ## Routing Types The API returns different quote types based on the optimal routing strategy: | Value | Type | Description | |-------|------|-------------| | 0 | CLASSIC | Standard AMM swap through Uniswap pools | | 1 | DUTCH_LIMIT | Dutch auction order (UniswapX) | | 2 | DUTCH_V2 | Dutch auction V2 | | 3 | LIMIT_ORDER | Limit order | | 4 | WRAP | ETH to WETH wrap | | 5 | UNWRAP | WETH to ETH unwrap | | 6 | BRIDGE | Cross-chain bridge | | 7 | PRIORITY | MEV-protected priority order | | 8 | DUTCH_V3 | Dutch auction V3 | | 9 | QUICKROUTE | Fast approximation quote | | 10 | CHAINED | Multi-step cross-chain swap | ## Getting Started 1. **Get an API Key**: Contact the Uniswap team to request API access 2. **Set Up Infrastructure**: Configure your RPC provider and wallet integration 3. **Implement the Flow**: Follow the [integration guide](./integration-guide) for step-by-step implementation 4. **Test on Testnet**: Validate your integration before going live ## Next Steps - [Integration Guide](./integration-guide) - Complete step-by-step implementation guide - [Quote Endpoint](./quote) - Detailed quote request/response documentation - [Permit2 Flow](./permit2) - Gasless approvals via EIP-712 signatures - [Error Handling](./errors) - Common errors and troubleshooting --- ## Get Funded ## Unichain Grant Programs Whether you’re playing with a new prototype or scaling your next venture, the Uniswap ecosystem provides [grants and support programs](https://www.uniswapfoundation.org/build) for developers at every stage. [The Uniswap Foundation](https://www.uniswapfoundation.org/), alongside key ecosystem contributors, offers structured grants and support pathways to help teams grow: from idea to impact. Each program is designed to meet you where you are, with a clear path to level up as you build. ## Before You Apply 1. **Deploy to Unichain (and/or v4).** Some programs will consider early prototypes, but being deployed on Unichain and/or Uniswap v4 will give you an advantage. Verify that your contract is deployed and functional on Unichain. 2. **Provide documentation.** Make sure that your application includes clear documentation including setup instructions, project goals, and demo materials. Ensure that your application includes a README.md and a working demo. 3. **Highlight your impact.** Measure and include metrics like user adoption, transaction volume, or community engagement. Share early data and qualitative user feedback to strengthen your application! ## Unichain Grants Unichain DeFi projects with $250K+ TVL and infrastructure projects can apply for product and GTM support, with up to $7.5K in grants funding. [Apply for Unichain Grant support](https://share.hsforms.com/18Kv3hTvDSt-x1wK9va0OYwsdca9) **What you get:** - Grant range: up to $7.5K USD - Application type: DeFi or infrastructure projects ## Uniswap Hook Incubator Ready to ship your first hook? The Uniswap Hook Incubator is an intensive, async program designed to help builders speedrun v4 expertise. Participants will learn how to create and launch high impact hooks, with the opportunity to win up to $40k in prizes for capstone projects. [Apply for the Uniswap Hook Incubator](https://atrium.academy/uniswap) **Ideal candidates:** - Are experienced Solidity developers - Have DeFi experience - Are passionate about exploring and building new primitives **What you get:** - Fully covered tuition - Up to $40k in prizes - Access to DeFi experts and the largest Uniswap v4 builder community ## Hook Design Lab The Uniswap v4 Hook Design Lab is a pilot program structured to support emerging DeFi builders: helping teams bring new ecosystem primitives from idea to mainnet with technical mentorship, GTM support, and milestone-based funding. [Apply to the Hook Design Lab.](https://share.hsforms.com/1y57UxXGGSrKc7d8v5opZzwsdca9) **Ideal projects:** - Bring a unique approach to capital efficiency, LP experience, target asset classes, and/or composability - Create public goods and infrastructure for the community to build on - Demonstrate the potential for ecosystem-wide impact A full list of builder requests can be found [here](https://www.uniswapfoundation.org/blog/uniswap-v4-supercharging-defi-across-chains). **What you get:** - Grant funding for GTM and growth campaigns - Technical, product design, and marketing support - Holistic integration and routing support ## UFSF Audit Subsidies Ready to launch? The Uniswap Foundation Security Fund provides access to best-in-class security audits at reduced or no cost, streamlining the process through a vetted pool of providers with transparent pricing and quality standards. [Apply for UFSF audit subsidies](https://areta.fillout.com/UFSF) **What you get:** - Access to premium audit slots with top providers - Standardized deliverables and quality assurance - For subsidy recipients: Up to 100% of audit costs ## Uniswap v4 Router Rebates Calling all routers, solvers and aggregators: you can claim daily subsidies for routing flow to hooked pools. [Apply for Uniswap v4 router rebates](https://sdca9.share.hsforms.com/2usOunBjQSZqrsVypvYdtQQ?1) **What you get:** - Daily subsidies covering up to 80% of gas fees per swap, capped at 50 gwei per transaction ## OP Superchain Grants Already launched on Unichain? Unichain and Superchain builders are eligible for Retro Funding and Funding Missions. Retro funding rewards are delivered monthly. [OP Grant Atlas](https://atlas.optimism.io/) **Funding focus:** - Onchain apps: DeFi, NFTs, gaming, and more - Developer tooling: tools, SDKs, APIs, and Superchain infrastructure. **What you get:** - Ecosystem recognition - Up to 8M in grants funding --- ## Security Resources ## Uniswap community security resources New apps can introduce new, and sometimes complex, security requirements. [The Uniswap Foundation](https://www.uniswapfoundation.org/) is committed to making security audits accessible, through grant programs like the Uniswap Foundation Security Fund. From audit subsidies to Safe Harbor, developers have access to a spectrum of security resources to ensure that they can ship safely. ## UFSF Audit Subsidies The Uniswap Foundation Security Fund provides access to best-in-class security audits at reduced or no cost, streamlining the process through a vetted pool of providers with transparent pricing and quality standards. [Apply for UFSF audit subsidies](https://areta.fillout.com/UFSF) ## Areta Market Areta Market lets you book your audit and get fast, transparent quotes from 20+ top-tier security auditors, all in one place. Eligible projects can apply for UFSF audit subsidies. [Explore Areta Market](https://areta.market/uniswap/) ## SEAL 911 (Security Alliance) Use the SEAL 911 bot during an emergency situation to automatically be connected with the best security specialists in the space. [Send message](https://www.securityalliance.org/seal-911) ## Safe Harbor (Security Alliance) By adopting the whitehat safe harbor agreement, whitehats can rescue your protocol correctly, without worrying about any legal repercussions. [Adopt Safe Harbor](https://www.securityalliance.org/safe-harbor) ## Wargames (Security Alliance) Prevent losses and mitigate threats with incident response training exercises tailored to your protocol. [Participate in Wargames](https://www.securityalliance.org/wargames) --- ## Ambassadors ## Get tailored support for your project [Uniswap Foundation Ambassadors](https://github.com/uniswapfoundation/UF-Ambassadors) are exceptional builders with deep expertise and passion for DeFi. They work closely with the Foundation team to grow the ecosystem and support the Uniswap community. UF Ambassadors represent a global volunteer network of DeFi builders and experts. They provide ongoing local support for builders: organizing events and meetups, creating tutorials and guides, and offering 1:1 developer mentorship. ## Meet Our Ambassadors ### [Nico Acosta](https://github.com/NicoAcosta) (Buenos Aires, Argentina) - GitHub: [@NicoAcosta](https://github.com/NicoAcosta) - Region: South America ### [Rafa Canseco](https://x.com/0xRafaCC) (Puebla, México) - GitHub: [@rafa-canseco](https://github.com/rafa-canseco) - Region: North America ### [Constantino Mora](https://x.com/constacrypto) (Ciudad de México, México) - GitHub: [@constantino](https://github.com/Constantino) - Region: North America ### [Mohak Gupta](https://x.com/mohak_sol) (Jaipur, India) - GitHub: [@mo-hak](https://github.com/mo-hak) - Region: South Asia ### [Rudransh](https://x.com/rudransh190204) (Jaipur, India) - GitHub: [@Ansh1902396](https://github.com/Ansh1902396) - Region: South Asia ### [Nobuaki Onishi](https://twitter.com/zak3939) (Tokyo, Japan) - GitHub: [@ZaK3939](https://github.com/ZaK3939) - Region: Asia-Pacific ### [Longs Gotar](https://x.com/devlongs_) (Lagos, Nigeria) - GitHub: [@devlongs](https://github.com/devlongs) - Region: Africa ### [Ivan Volovyk](https://x.com/LisVikkk) (Warsaw, Poland) - GitHub: [@ivanvolov](https://github.com/ivanvolov) - Region: Europe ### [Tony Lau](https://x.com/intrepid_crypto) (Toronto, Canada) - GitHub: [@intrepidcanadian](https://github.com/intrepidcanadian) - Region: North America ## Meet up at Uniswap community events The Uniswap Foundation and UF Ambassador teams regularly host global events, hackathons, workshops, and meetups. Explore the Uniswap Foundation event calendar [here](https://lu.ma/uniswap-foundation). --- ## Overview(Liquidity-launchpad) # Introduction & Overview Looking to participate in active auctions? Check them out on the [Uniswap Frontend](https://app.uniswap.org/explore/auctions/). ## What is the Uniswap Liquidity Launchpad? The Uniswap Liquidity Launchpad is a comprehensive framework for bootstrapping initial liquidity for Uniswap V4 pools through fair, transparent price discovery (see whitepaper). It combines three critical functions into a single, composable system: 1. **Price Discovery** - Run fair auctions using a novel Continuous Clearing Auction (CCA) mechanism to establish market price 2. **Liquidity Bootstrapping** - Automatically seed Uniswap V4 pools with auction proceeds at the discovered price 3. **Token Creation** (Optional) - Deploy new ERC-20 tokens with rich metadata and optional cross-chain capabilities Unlike traditional approaches that rely on centralized market makers or expose participants to timing games and manipulation, the Uniswap Liquidity Launchpad provides an open mechanism for boostrapping deep liquidity on decentralized exchanges. The system is composable - it is not limited to the intial set of implementation contracts. Other auction and LBPStrategy implementations are welcome! ### Key Benefits - **Fair Price Discovery** - Continuous clearing auctions eliminate timing games and establish credible market prices - **Immediate Deep Liquidity** - Seamless transition from price discovery to active Uniswap V4 trading with substantial initial depth - **Permissionless** - Anyone can bootstrap liquidity or participate in price discovery without gatekeepers - **Transparent** - All parameters are immutable after they are set - **Composable** - Modular architecture supports multiple auction formats and distribution strategies - **Distribution** - Users can participate in auctions via interfaces acros the ecosystem including the [Uniswap Frontend](https://app.uniswap.org/explore/auctions/) and custom interfaces ## Components The Uniswap Liquidity Launchpad framework is built on three coordinated components that work together to bootstrap liquidity: 1. **[Liquidity Launcher →](https://github.com/Uniswap/liquidity-launcher)** Central orchestration contract that coordinates distribution and liquidity deployment 2. **[Token Factory →](https://github.com/Uniswap/uerc20-factory)** (Optional) Creates new ERC-20 tokens with metadata, or integrates existing tokens 3. **Liquidity Strategies** - Modular contracts for different price discovery and liquidity mechanisms (prebuilt [LBP Strategy](https://github.com/Uniswap/liquidity-launcher) or [custom strategies](./05-strategies.md#writing-a-custom-strategy)) Each component is designed to be composable and extensible, allowing you to customize your liquidity bootstrapping while maintaining security and fairness guarantees. ## High-Level Architecture ![Token Launcher Architecture](./images/TokenLauncherOverview.png) ### Example Flow The following is a high level overview of how the provided [LBP Strategy](https://github.com/Uniswap/liquidity-launcher) contracts interface and work with the [Continuous Clearing Auction](https://github.com/Uniswap/continuous-clearing-auction/). The following actions must be performed atomically in one transaction. `LiquidityLauncher` supports native multicall which is highly recommended. 1. **Prepare Token** (Optional) Launch a new token using `LiquidityLauncher.createToken()` via the [UERC20Factory](https://github.com/Uniswap/liquidity-launcher/blob/96860d8239785e717cff1e4189643b9acee925ff/src/token-factories/uerc20-factory), which deploys a UERC20 or UERC20Superchain token and mints the initial supply to the launcher. Alternatively, use an existing token and approve the launcher to transfer it via Permit2. 2. **Deploy Strategies** Call `LiquidityLauncher.distributeToken()` to deploy a new LBPStrategy instance via factory. The strategy will validate that the auction parameters and eventual pool configuration are valid, and if so, it will deploy a CCA auction with the desired amount of tokens to sell. The `LiquidityLauncher` contract will transfer tokens to the LBPStrategy and then they will be transferred into the auction. We use an optimistic transfer then call pattern throughout the contracts to trigger an action after performing an ERC20 transfer. 3. **Auction Completion** When the auction ends, all of the raised funds will be swept to a specified `fundsRecipient`. The LBPStrategy will ensure that it is the recipient of both the raised funds and any leftover unsold tokens. The configured `initializer` on the LBPStrategy must implement the [ILBPInitializer](https://github.com/Uniswap/liquidity-launcher/blob/main/src/interfaces/ILBPInitializer.sol) interface to return the necessary data for the pool migration and liquidity creation. The `initializer` returns parameters like the initial price, tokens sold, and currency raised which are used to initialize the pool. 4. **Migrating Liquidity** Anyone can call the `migrate()` function on the `LBPStrategy` after the configured `migrationBlock`. This does the following: - Initialize a new Uniswap V4 pool at the price from the auction - Deploy a full-range LP position using the auction proceeds + reserved tokens - (Optionally) deploy a one-sided position with remaining tokens - Mint the NFT for the LP position to the a specified `positionRecipient` - Sweep any leftover tokens or raised funds to a configured `operator` 5. **After Migration** The pool will be live on Uniswap V4 with deep liquidity around the discovered price. Participants in the auction can claim their purchased tokens on the auction after `claimBlock`, and these instances of the LBPStrategy + Auction contracts should hold no funds after all bids are withdrawn and all actions performed. ## Next Steps - Learn about the [Continous Clearing Auction](./04-auction-mechanism.md) mechanism - Read the whitepaper to learn more about the mechanism - Review the Continuous Clearing Auction [Technical Reference](https://github.com/Uniswap/continuous-clearing-auction/blob/main/docs/TechnicalDocumentation.md) --- ## Deployments View the changelogs for more details on differences between contract deployments: [continuous-clearing-auction](https://github.com/Uniswap/continuous-clearing-auction/blob/main/CHANGELOG.md) and [liquidity-launcher](https://github.com/Uniswap/liquidity-launcher/blob/main/CHANGELOG.md). ## ContinuousClearingAuctionFactory The CCA factory has no constructor parameters so it can be deployed to the same address across all compatible chains. It is currently deployed to Ethereum Mainnet, Unichain, Base, Arbitrum, and Sepolia testnet. | Network | Address | Commit Hash | Version | | -------- | ------------------------------------------ | ---------------------------------------- | ---------------- | | v1.1.0 | 0xCCccCcCAE7503Cac057829BF2811De42E16e0bD5 | 87b2546a298f691c095b06ec077ceef25ba56007 | v1.1.0 | | v1.0.0\* | 0x0000ccaDF55C911a2FbC0BB9d2942Aa77c6FAa1D | 154fd189022858707837112943c09346869c964f | v1.0.0-candidate | > \*v1.0.0-candidate is the initial version of CCA and is NOT recommended for production use. See the [Changelog](https://github.com/Uniswap/continuous-clearing-auction/blob/main/CHANGELOG.md) for more details. ## LiquidityLauncher The LiquidityLauncher is a singleton contract which is delployed to the same address across all compatible chains. | Version | Address | Commit Hash | | -------- | ------------------------------------------ | ---------------------------------------- | | v1.0.0 | 0x00000008412db3394C91A5CbD01635c6d140637C | fd5be9b7a918ca3d925d985dff9bcde82b3b8a9d | ## LBP Strategies LBP strategies are deployed via factory contracts which are deployed to different addresses on different chains. Make sure to use the correct factory contract for the chain in question. ### FullRangeLBPStrategyFactory The FullRangeLBPStrategyFactory is a factory contract for the [FullRangeLBPStrategy](https://github.com/Uniswap/liquidity-launcher/blob/main/src/strategies/lbp/FullRangeLBPStrategy.sol). | Version | Chain | Address | Commit Hash | |---------|-------|---------|------------| | v2.0.0 | Mainnet | 0x65aF3B62EE79763c704f04238080fBADD005B332 | 610603eed7c35ff504e23ec87cd18ec3f701e746 | | v2.0.0 | Unichain | 0xAa56d4d68646B4858A5A3a99058169D0100b38e2 | 610603eed7c35ff504e23ec87cd18ec3f701e746 | | v2.0.0 | Base | 0x39E5eB34dD2c8082Ee1e556351ae660F33B04252 | 610603eed7c35ff504e23ec87cd18ec3f701e746 | | v2.0.0 | Sepolia | 0x89Dd5691e53Ea95d19ED2AbdEdCf4cBbE50da1ff | 610603eed7c35ff504e23ec87cd18ec3f701e746 | ### AdvancedLBPStrategyFactory The AdvancedLBPStrategyFactory is a factory contract for the [AdvancedLBPStrategy](https://github.com/Uniswap/liquidity-launcher/blob/main/src/strategies/lbp/AdvancedLBPStrategy.sol). | Version | Chain | Address | Commit Hash | |---------|-------|---------|------------| | v2.0.0 | Mainnet | 0x982DC187cbeB4E21431C735B01Ecbd8A606129C5 | 610603eed7c35ff504e23ec87cd18ec3f701e746 | | v2.0.0 | Unichain | 0xeB44195e1847F23D4ff411B7d501b726C7620529 | 610603eed7c35ff504e23ec87cd18ec3f701e746 | | v2.0.0 | Base | 0x9C5A6fb9B0D9A60e665d93a3e6923bDe428c389a | 610603eed7c35ff504e23ec87cd18ec3f701e746 | | v2.0.0 | Sepolia | 0xdC3553B7Cea1ad3DAB35cBE9d40728C4198BCBb6 | 610603eed7c35ff504e23ec87cd18ec3f701e746 | ## Previous Deployments The following contracts are deprecated and are not recommended for production use. See the [Changelog](https://github.com/Uniswap/liquidity-launcher/blob/main/CHANGELOG.md) for more details. ### LBPStrategyBasicFactory The LBPStrategyBasicFactory is a factory contract for the [LBPStrategyBasic](https://github.com/Uniswap/liquidity-launcher/blob/v1.0.0-candidate/src/distributionContracts/LBPStrategyBasic.sol). | Network | Address | Commit Hash | Version | |---------|---------|------------|---------| | Mainnet | 0xbbbb6FFaBCCb1EaFD4F0baeD6764d8aA973316B6 | fd5be9b7a918ca3d925d985dff9bcde82b3b8a9d | v1.0.0-candidate | | Base | 0xC46143aE2801b21B8C08A753f9F6b52bEaD9C134 | fd5be9b7a918ca3d925d985dff9bcde82b3b8a9d | v1.0.0-candidate | | Unichain | 0x435DDCFBb7a6741A5Cc962A95d6915EbBf60AE24 | fd5be9b7a918ca3d925d985dff9bcde82b3b8a9d | v1.0.0-candidate | ### VirtualLBPStrategyFactory | Network | Address | Commit Hash | Version | |---------|---------|------------|---------| | Mainnet | 0x00000010F37b6524617b17e66796058412bbC487 | fd5be9b7a918ca3d925d985dff9bcde82b3b8a9d | v1.0.0-candidate | | Sepolia | 0xC695ee292c39Be6a10119C70Ed783d067fcecfA4 | fd5be9b7a918ca3d925d985dff9bcde82b3b8a9d | v1.0.0-candidate | ## UERC20Factory The UERC20Factory is a factory contract for new UERC20 tokens. | Version | Address | Commit Hash | | -------- | ------------------------------------------ | ---------------------------------------- | | v1.0.0 | 0x0cde87c11b959e5eb0924c1abf5250ee3f9bd1b5 | 9705debfea9e6a641bc04352398f9e549055ac44 | ## USUPERC20Factory The USUPERC20Factory is a factory contract for new Superchain compatible UERC20 tokens. It is deployed to the same address across all Superchain compatible L2s. | Version | Address | Commit Hash | | -------- | ------------------------------------------ | ---------------------------------------- | | v1.0.0 | | 9705debfea9e6a641bc04352398f9e549055ac44 | --- ## Continuous Clearing Auction # Continuous Clearing Auction (CCA) **Repository:** [github.com/Uniswap/continuous-clearing-auction](https://github.com/Uniswap/continuous-clearing-auction) The Continuous Clearing Auction (CCA) is a novel auction mechanism that generalizes the uniform-price auction into continuous time. It provides fair price discovery for bootstrapping initial liquidity while eliminating timing games and encouraging early participation (see [whitepaper](/whitepaper_cca.pdf)). ## Overview Bootstrapping initial liquidity for new tokens is challenging. Traditional approaches suffer from various weaknesses: - **Fixed-price sales** lead to mispricing and priority races, creating thin or unstable liquidity - **Dutch auctions** create timing games and favor professionals over genuine participants - **One-shot auctions** enable demand reduction and last-minute sniping - **Bonding curves** are path-dependent and vulnerable to manipulation - **Centralized market makers** require trust and extract significant value CCA addresses these issues through a unique approach: **automatic bid spreading over time** combined with **continuous price discovery**. ### Mechanism overview For a detailed overview, please read the [whitepaper](/whitepaper_cca.pdf). The most important element to understand about a Continuous Clearing Auction (CCA) is that tokens are sold over time to the current set of active participants. Participants are comprised of two things, a budget and a max price. The clearing price of the auction in a block is the price which all bidders in that block pay. This is the same concept as in uniform price auctions. But in CCA this price is gradually discovered over time. Every block, some number of tokens (as defined by the configured release schedule) are allocated to bids with higher max prices, then those with lower max prices. Because we require users to specify a maximum price, there exists a clearing price for which there are not enough "active" participants in the auction to purchase all of the tokens that are being sold, since a bid is removed from the auction once it falls below the clearingPrice. The current price of the auction will always be just below this price, ensuring that all of the supply can be sold to the current set of bids. At a high level it has these benefits: - No participant can concentrate demand at a single moment - Timing of bid submission matters less than valuation - Early bidders naturally gain more exposure to lower prices - Sniping and last-minute gaming become ineffective ## Technical overview Check out the full [technical reference](https://github.com/Uniswap/continuous-clearing-auction/blob/main/docs/TechnicalDocumentation.md) ## Integration guidelines CCA is a very flexible protocol. Always validate the parameters of an auction before participating. Additionally, integrators and teams building on CCA should be aware of the following [integration guidelines](https://github.com/Uniswap/continuous-clearing-auction/blob/main/docs/TechnicalDocumentation.md#integration-guidelines). ## License The Continuous Clearing Auction contracts are MIT licensed. --- ## Strategies # Understanding liquidity strategies Liquidity strategies are modular contracts for different price discovery and liquidity mechanisms. The stock contracts shipped with the Uniswap Liquidity Launchpad deploy new Continuous Clearing Auction (CCA) auctions and create Uniswap v4 LP pools with the proceeds. This is only one of many possible strategies. We expect there to be many different strategies for different use cases. ## LBPStrategyBase The LBPStrategyBase is an abstract base contract for liquidity bootstrapping (LBP) strategies. It provides core functionality for deploying auctions and initializing LP pools. ### Overview Contracts inheriting from this base contract will be able to: - Deploy a new Continuous Clearing Auction (CCA) auction - Create a Uniswap v4 LP pool with the proceeds from the auction - Send the LP position to a specified `positionRecipient` - Sweep any leftover tokens or raised funds to a configured `operator` The amount of tokens and currency used for the LP pool will vary depending on the final price of the auction and how much currency was raised. The following functions are left virtual to be implemented by the inheriting contract: - `_createPositionPlan`: Creates the calldata for Uniswap v4 Position Manager (POSM) - `_getTokenTransferAmount`: Calculates the amount of tokens to transfer to POSM - `_getCurrencyTransferAmount`: Calculates the amount of currency to transfer to POSM For example, if only a full range position is desired, the inheriting contract should create a full range position plan and transfer the full amount of tokens and currency to Position Manager. See [FullRangeLBPStrategy](TODO) for an example implementation. ### Constructor parameters The LBPStrategyBase constructor takes the following parameters: ```solidity /// LBPStrategyBase.constructor constructor( address _token, uint128 _totalSupply, MigratorParameters memory _migratorParams, bytes memory _auctionParams, IPositionManager _positionManager, IPoolManager _poolManager ) ``` - `_token`: The address of the token to be bootstrapped. - `_totalSupply`: The total amount of tokens to be used for both the auction and LP pool. - `_migratorParams`: The parameters for the pool migration. - `_auctionParams`: The parameters for the created auction. - `_positionManager`: The v4 position manager. - `_poolManager`: The v4 pool manager. During construction, the contract validates its parameters and calculates the number of tokens to send to the auction vs. reserve for the LP pool. This split is customizable within `MigratorParameters`: ```solidity // src/types/MigratorParameters.sol struct MigratorParameters { uint64 migrationBlock; address currency; uint24 poolLPFee; int24 poolTickSpacing; uint24 tokenSplitToAuction; address auctionFactory; address positionRecipient; uint64 sweepBlock; address operator; uint128 maxCurrencyAmountForLP; } ``` - `migrationBlock`: The block number when `migrate()` can be called. - `currency`: The currency that the token will be paired with in the v4 pool (currency that the auction raised funds in). - `poolLPFee`: The LP fee that the v4 pool will use. - `poolTickSpacing`: The tick spacing that the v4 pool will use. - `tokenSplitToAuction`: The percentage of the total supply of the token that will be sent to the auction, expressed in mps (1e7 = 100%). - `auctionFactory`: The Auction factory that will be used to create the auction. - `positionRecipient`: The address that will receive the position. - `sweepBlock`: The block number when the operator can sweep currency and tokens from the pool. - `operator`: The address that is able to sweep currency and tokens from the pool. - `maxCurrencyAmountForLP`: The maximum amount of currency that can be used for LP. ### migrate The `migrate()` function initializes the liquidity pool in v4. It validates that the auction has concluded, calculates the amount of tokens and currency to transfer to Position Manager, and calls POSM to create the pool and position. This function can be called by anyone after the configured `migrationBlock`. ### sweepToken() and sweepCurrency() The `sweepToken()` and `sweepCurrency()` functions sweep any leftover tokens or raised funds to a configured `operator`. In many cases, these will be amounts intended for the team. Sweeping can only be done after the configured `sweepBlock`, which must be after the `migrationBlock`. Users should ensure there is enough time between `migrationBlock` and `sweepBlock` for the pool to be initialized and the tokens to be transferred to the position recipient before amounts are swept. ## FullRangeLBPStrategy The FullRangeLBPStrategy is a simple implementation of the LBPStrategyBase contract that creates a full range position in the liquidity pool. It takes no additional constructor parameters. ## AdvancedLBPStrategy The AdvancedLBPStrategy is a more advanced implementation of the LBPStrategyBase contract that allows for the creation of one-sided liquidity positions in addition to the full range one. It takes the following additional constructor parameters: ```solidity constructor( ... // constructor parameters for LBPStrategyBase bool _createOneSidedTokenPosition, bool _createOneSidedCurrencyPosition ) ``` - `_createOneSidedTokenPosition`: True if a one-sided token position should be created. - `_createOneSidedCurrencyPosition`: True if a one-sided currency position should be created. If either of these are set to `true`, the contract will create a one-sided liquidity position in either token or currency with any remaining tokens or currency after the full range position is created. The operator should not expect to receive any swept tokens or currency after the migration. ## GovernedLBPStrategy The GovernedLBPStrategy is an extension of the FullRangeLBPStrategy that prevents swapping on the LP pool until approved by governance (or some other permissioned actor). It takes the following additional constructor parameters: ```solidity constructor( ... // constructor parameters for FullRangeLBPStrategy address _governance ) ``` - `_governance`: The address that is allowed to approve swaps on the LP pool. ## Writing a custom strategy To create a custom strategy, inherit from the LBPStrategyBase contract to get the core functionality associated with creating an auction and LP pool. Implement the `_createPositionPlan` function to create the calldata for the Uniswap v4 Position Manager (POSM) to create the position. Also implement `_getTokenTransferAmount` and `_getCurrencyTransferAmount` as needed to ensure the correct amount of tokens and currency are transferred. Be aware of the following considerations: - `LBPStrategyBase` does not require that `migrate()` is called before `sweepToken()` or `sweepCurrency()`. Any strategy written should ensure there is ample time between the end of the auction and the migration so the pool can be initialized and the tokens can be transferred to the position recipient before amounts are sent to the operator. - `migrate()` can fail to create a full range position in rare cases. Be sure to validate the returned values from the auction. --- ## Custom Validation Hook This guide will outline how to create a custom validation hook for a CCA auction. Hooks are powerful extensions to CCA which unlock a wide range of features like bidding limits, allowlisting, and more. ## Prerequisites - [Foundry](https://getfoundry.sh/introduction/installation) - Basic understanding of the CCA contracts and Solidity If you haven't already, please check out the [quickstart guide](../quickstart/local-deployment.md). ## Interface All validation hooks must implement the [IValidationHook](https://github.com/Uniswap/continuous-clearing-auction/blob/main/src/interfaces/IValidationHook.sol) interface. ```solidity interface IValidationHook { function validate(uint256 maxPrice, uint128 amount, address owner, address sender, bytes calldata hookData) external; } ``` This function is called by the auction contract during bid submission. To reject a bid, your hook MUST revert. ### Parameters - `maxPrice`: The maximum price the bidder is willing to pay - `amount`: The amount of currency the user is bidding - `owner`: The address of the user who will receive any purchased tokens or refunded currency - `sender`: The address of the user who is submitting the bid - `hookData`: Any extra data Note that `hookData` is arbitrarily specified by the bidder and should NOT be trusted. ## Introspection It is highly recommended to implement basic [ERC165](https://eips.ethereum.org/EIPS/eip-165) introspection support for your hook. This will allow users to query for which capabilities the hook supports. ```solidity interface IERC165 { function supportsInterface(bytes4 interfaceId) external view returns (bool); } ``` You can also inherit from the base contract [ValidationHookIntrospection](https://github.com/Uniswap/continuous-clearing-auction/blob/main/src/periphery/validationHooks/ValidationHookIntrospection.sol) which automatically implements support for `IERC165` and `IValidationHook`. Make sure to override the `supportsInterface` function to return `true` for your specific hook interface. ```solidity interface ICustomValidationHook is IValidationHook, IERC165 { // Your custom hook interface here } contract CustomValidationHook is ValidationHookIntrospection { function supportsInterface(bytes4 interfaceId) public view override(ValidationHookIntrospection, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || interfaceId == type(ICustomValidationHook).interfaceId; } } ``` ## Deployment Hooks must be deployed before the auction is created as they must be set in the `validationHook` parameter of the auction. If your hook requires that only the auction is able to call `validate`, you can initialize the hook after deployment. --- ## Supply schedule This guide will cover what a supply schedule is in the context of CCA and how it can be configured. ## Prerequisites - [Foundry](https://getfoundry.sh/introduction/installation) - Basic understanding of the CCA contracts and Solidity If you haven't already, please check out the [quickstart guide](../quickstart/local-deployment.md). ## High-level overview The supply schedule defines the _rate_ of token issuance over the course of the auction. The auction creator can configure exactly how many tokens are sold at each block of the auction. A well-designed supply schedule does three things at once: it **creates meaningful incentives to participate early**, **keeps the auction attractive for late arrivals**, and **anchors a robust final clearing price**. In practice, this is best achieved with a **moderately convex supply curve** that releases supply gradually, combined with a **large final block** of tokens. Early supply is sufficient to reward early bidders, later supply preserves participation throughout the auction, and the large end block ensures that the final clearing price reflects broad demand and is difficult to manipulate. ## Implementation The supply schedule is implemented as a series of `uint64` values that represent the per-block issuance rate in MPS (milli-bips), and the number of blocks the rate is valid for. For example, the following supply schedule: `(100e3, 50), (200e3, 25)` represents a supply schedule where `100,000 mps` is sold each block for `50` consecutive blocks, and `200,000 mps` is sold each block for the next `25` blocks. > Note that 1 mps represents one thousandth of a basis point, with one million mps representing 100% of the supply. Thus, the above schedule sells 1% per block for the first 50 blocks, and 2% per block for the next 25 blocks, resulting in selling 1 * 50 + 2 * 25 = 100% of the supply. This schedule is _valid_ since the cumulative issuance rate is 100%. ### Encoding The supply schedule is encoded as a packed bytes array of `uint64` values. Each `uint64` value represents an encoded pair of `(uint24 mps, uint40 blocks)`. The `mps` value is the per-block issuance rate in MPS, and the `blocks` value is the number of blocks the rate is valid for. Use `abi.encodePacked` to pack the `uint64` values into a bytes array. You can leverage the [AuctionStepsBuilder](https://github.com/Uniswap/continuous-clearing-auction/blob/main/test/utils/AuctionStepsBuilder.sol) helper library to build the auction steps data locally. ### Usage The supply schedule is passed to the auction as a parameter within the `AuctionParameters` struct when deploying a new auction as the `auctionStepsData` parameter. ## Guidelines Integrators and teams should be aware of the following best practices when designing a supply schedule: - Schedules should be monotonically increasing (each step having a higher % rate than the previous step) - Early steps should not sell a significant percentage of the supply - The last step should be **one block** in length and should sell a **significant** percentage of the supply ### Price durability Since the final clearing price becomes the starting price of the token on the AMM, it is crucial that the final block sells a significant percentage of the supply. This makes manipulation of the final clearing price difficult. Additionally, it still allows for late bidders to receive a meaningful amount of tokens. There is no single correct choice, but in practice the end block should be **large enough that the final clearing price cannot be meaningfully moved by new bids**, while not so large that it dominates the entire auction. As a rough guideline: - End blocks on the order of **20–40% of total supply** tend to work well across a wide range of market conditions. - Below this range, the final price becomes increasingly sensitive to small amounts of late demand. - Above this range, the auction begins to resemble a single-period sale, weakening early-participation incentives. The appropriate size ultimately depends on the expected scale of participation, the importance of the final price as a market reference, and the level of manipulation resistance desired. ### (Optional) zero percentage steps It's possible to configure the schedule to have multiple steps with a 0% issuance rate. If included in the beginning of the schedule, it functions as a **pre-bid** phase where bidders can bid before tokens start being sold. This is beneficial for large auctions as it reduces the chance that tokens are left unsold in earlier blocks of the auction. --- ## Example configuration # Configuring a CCA auction This section will walk you through each parameter of a CCA auction and an example configuration. For more details please refer to the [technical reference](/docs/contracts/liquidity-launchpad/05-auction-mechanism.md). ### Prerequisites Basic understanding of the CCA auction mechanism and Solidity is assumed. This guide continues from the [previous section](/docs/contracts/liquidity-launchpad/quickstart/local-deployment.md). ## Auction Parameters The `AuctionParameters` struct parameterizes a new CCA auction. It is encoded and passed to the `ContinuousClearingAuctionFactory` contract when deploying a new auction. The struct definition is as follows: ```solidity /// from: https://github.com/Uniswap/continuous-clearing-auction/blob/main/src/interfaces/IContinuousClearingAuction.sol struct AuctionParameters { address currency; // token to raise funds in. Use address(0) for ETH address tokensRecipient; // address to receive leftover tokens address fundsRecipient; // address to receive all raised funds uint64 startBlock; // Block which the first step starts uint64 endBlock; // When the auction finishes uint64 claimBlock; // Block when the auction can claimed uint256 tickSpacing; // Fixed granularity for prices address validationHook; // Optional hook called before a bid uint256 floorPrice; // Starting floor price for the auction uint128 requiredCurrencyRaised; // Amount of currency required to be raised for the auction to graduate bytes auctionStepsData; // Packed bytes describing token issuance schedule } ``` We'll cover each parameter in detail below. ### currency The `currency` parameter is the address of the token that will be used to raise funds for the auction. This can be any ERC20 token or the native token of the chain (address(0)). ### tokensRecipient The `tokensRecipient` parameter is the address that will receive the leftover tokens after the auction is complete. Depending on the implementation, this may be a trusted EOA address or a strategy contract address if the auction is being used to bootstrap a liquidity pool. ### fundsRecipient The `fundsRecipient` parameter is the address that will receive the funds raised during the auction. Again, depending on the implementation, this may be a trusted EOA address or a strategy contract address if the auction is being used to bootstrap a liquidity pool. ### startBlock The `startBlock` parameter is the block number at which the auction will start. Once the auction starts, the supply schedule will begin and bids will be accepted. Note that `startBlock` is inclusive so the auction will start exactly on the block specified. ### endBlock The `endBlock` parameter is the block number at which the auction will end. Note that `endBlock` is exclusive so the auction will end on the block specified. No more bids are accepted at and after the end block. ### claimBlock The `claimBlock` parameter is the block number at which purchased tokens can be claimed. It must be at or after the `endBlock`. ### tickSpacing The `tickSpacing` parameter denotes the **minimum** price increment for bids. It is used to prevent users from being outbid by others by infinitesimally small amounts and for gas efficiency in finding new clearing prices. Generally integrators should choose a tick spacing of AT LEAST 1 basis point of the floor price. 1% or 10% is also reasonable. Bids can only be placed at increments of tickSpacing, but the auction may clear at any price. ### validationHook The `validationHook` parameter is an optional contract that can be used to validate bids before they are accepted. It is called before a bid is accepted and can be used to reject bids that do not meet certain criteria. It must implement the `IValidationHook` interface. Use `address(0)` to opt-out of validation. ### floorPrice The `floorPrice` parameter is the starting floor price for the auction. It is the minimum price at which bids will be accepted. All prices in the auction are represented as the ratio of `currency` to `token`. For example, a floor price with an integer component of 1000 means that 1000 `currency` is required to purchase 1 `token`. Additionally, the price is represented as a Q96 fixed-point number to allow for fractional prices. For more details about the Q-number format please refer to this wikipedia [article](https://en.wikipedia.org/wiki/Q_(number_format)). Using the above example, a floor price of 1000 would be represented as `1000 << 96` or `1000 * 2^96`. ### requiredCurrencyRaised The `requiredCurrencyRaised` parameter is the amount of `currency` required to be raised for the auction to graduate. If the auction does not raise this amount, the auction will not graduate and all bidders will be able to withdraw their initial bid amounts. No tokens will be sold and the `totalSupply` will be swept back to the `tokensRecipient`. ### auctionStepsData The `auctionStepsData` parameter is a packed bytes array that describes the token issuance schedule. It is used to determine the amount of tokens that will be sold in each block. It is a series of `uint64` values that represent the per-block issuance rate in MPS (milli-bips), and the number of blocks to sell over. For more details about the auction steps please refer to the [technical reference](/docs/contracts/liquidity-launchpad/05-auction-mechanism.md#auction-steps-supply-issuance-schedule). ## Example configuration Let's create an example configuration for a CCA auction. We'll use the script we started in the [previous section](/docs/contracts/liquidity-launchpad/quickstart/local-deployment.md). Here's the script copy and pasted for convenience: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract ExampleCCADeploymentScript is Script { function setUp() public {} function run() public { vm.startBroadcast(); ContinuousClearingAuctionFactory factory = new ContinuousClearingAuctionFactory(); console2.log("Factory deployed to:", address(factory)); // TODO: configure the auction and deploy it via `initializeDistribution()` vm.stopBroadcast(); } } ``` First, make sure to import the `AuctionParameters` struct from the `IContinuousClearingAuction` interface: ```solidity ``` Then, we can configure the auction parameters: ```solidity address deployer = vm.envAddress("DEPLOYER"); AuctionParameters memory parameters = AuctionParameters({ currency: address(0), // We'll use the native token for this example tokensRecipient: deployer, fundsRecipient: deployer, startBlock: uint64(block.number), // Start the auction on the current block endBlock: uint64(block.number + 100), // End the auction after 100 blocks claimBlock: uint64(block.number + 100), // Allow claims at the end of the auction tickSpacing: 79228162514264334008320, // Use a tick spacing equal to the floor price validationHook: address(0), // Use no validation hook floorPrice: 79228162514264334008320, // Use a floor price representing a ratio of 1:1,000,000 (1 ETH for 1 million tokens) requiredCurrencyRaised: 0, // No graduation threshold auctionStepsData: bytes("") // Leave this blank for now }); ``` Let's build the auction steps data. For simplicity, we'll sell tokens following a monotonically increasing schedule. We'll sell 10% over 50 blocks, 49% over 49 blocks, and the final 41% in the last block. See the [note about auction steps](/docs/contracts/liquidity-launchpad/05-auction-mechanism.md#note-on-auction-steps) for more details about the rationale behind this example schedule. To derive the steps: - First tranche: 10% over 50 blocks Express 10% in MPS as 1e6 (1,000,000). Over 50 blocks this is 1e6 / 50 = 20,000 MPS per block. Pack this into a bytes8 value: ```solidity bytes8 firstTranche = uint64(20_000) | (uint64(50) << 24); ``` Repeat this for the rest of the tranches to get the final auction steps data: ```solidity bytes8 secondTranche = uint64(100_000) | (uint64(49) << 24); // 49e6 / 49 = 100_000 MPS per block bytes8 thirdTranche = uint64(4_100_000) | (uint64(1) << 24); // 41e6 / 1 = 4_100_000 MPS per block ``` Finally, pack the auction steps data into a bytes array: ```solidity bytes memory auctionStepsData = abi.encodePacked(firstTranche, secondTranche, thirdTranche); // Set the auction steps data parameters.auctionStepsData = auctionStepsData; ``` You can leverage the [AuctionStepsBuilder](https://github.com/Uniswap/continuous-clearing-auction/blob/main/test/utils/AuctionStepsBuilder.sol) helper library to build the auction steps data. Before we can finish the script we need to deploy a MockERC20 token to use in the auction. You can use the `ERC20Mock` contract in `@openzeppelin/contracts/mocks/token/ERC20Mock.sol`, or any other MockERC20 token you prefer. ```solidity ``` Now let's finish the script: ```solidity using AuctionStepsBuilder for bytes; function run() public { address deployer = vm.envAddress("DEPLOYER"); vm.startBroadcast(); ContinuousClearingAuctionFactory factory = new ContinuousClearingAuctionFactory(); console2.log("Factory deployed to:", address(factory)); ERC20Mock token = new ERC20Mock(); uint256 totalSupply = 1_000_000_000e18; // 1 billion tokens bytes memory auctionStepsData = AuctionStepsBuilder.init().addStep(20_000, 50).addStep(100_000, 49).addStep(4_100_000, 1); AuctionParameters memory parameters = AuctionParameters({ currency: address(0), tokensRecipient: deployer, fundsRecipient: deployer, startBlock: uint64(block.number), endBlock: uint64(block.number + 100), claimBlock: uint64(block.number + 100), tickSpacing: 79228162514264334008320, validationHook: address(0), floorPrice: 79228162514264334008320, requiredCurrencyRaised: 0, auctionStepsData: auctionStepsData }); IDistributionContract auction = IDistributionContract(factory.initializeDistribution(address(token), totalSupply, abi.encode(parameters), bytes32(0))); token.mint(address(auction), totalSupply); console2.log("Auction deployed to:", address(auction)); vm.stopBroadcast(); } ``` This will deploy the mock token, deploy the auction contract, and mint the total supply (1 billion tokens) to the auction contract. The final step in the script is to ensure that we call `onTokensReceived()` on the auction contract to register the receipt of the tokens. ```solidity token.mint(address(auction), totalSupply); auction.onTokensReceived(); ``` The complete script should look like this: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract ExampleCCADeploymentScript is Script { using AuctionStepsBuilder for bytes; function setUp() public {} function run() public { address deployer = vm.envAddress("DEPLOYER"); vm.startBroadcast(); ContinuousClearingAuctionFactory factory = new ContinuousClearingAuctionFactory(); console2.log("Factory deployed to:", address(factory)); ERC20Mock token = new ERC20Mock(); uint256 totalSupply = 1_000_000_000e18; // 1 billion tokens bytes memory auctionStepsData = AuctionStepsBuilder.init().addStep(20_000, 50).addStep(100_000, 49).addStep(4_100_000, 1); AuctionParameters memory parameters = AuctionParameters({ currency: address(0), tokensRecipient: deployer, fundsRecipient: deployer, startBlock: uint64(block.number), endBlock: uint64(block.number + 100), claimBlock: uint64(block.number + 100), tickSpacing: 79228162514264334008320, validationHook: address(0), floorPrice: 79228162514264334008320, requiredCurrencyRaised: 0, auctionStepsData: auctionStepsData }); IDistributionContract auction = IDistributionContract(factory.initializeDistribution(address(token), totalSupply, abi.encode(parameters), bytes32(0))); token.mint(address(auction), totalSupply); auction.onTokensReceived(); console2.log("Auction deployed to:", address(auction)); vm.stopBroadcast(); } } ``` Let's run the script: ```bash forge script scripts/ExampleCCADeploymentScript.s.sol:ExampleCCADeploymentScript \ --rpc-url http://localhost:8545 --private-key --broadcast ``` The deployment should be successful and you should see the factory and auction contract addresses logged to the console. ### Next steps In the next section we'll write some scripts to interact with the deployed auction contract. --- ## Exit and claim tokens This section will walk you through exiting a bid and claiming purchased tokens on a CCA auction. ## Prerequisites This guide continues from the [previous section](/docs/contracts/liquidity-launchpad/quickstart/price-discovery.md). Basic knowledge of the CCA auction mechanism and Solidity is assumed. ## Summary Currently we have a CCA contract deployed which we have submitted a bid to. We're the only bidder in the auction and the clearing price of the auction is at our max price. Let's modify the script to add another bid which will outbid our initial one, and show how both bids can be exited. ## Exiting a bid From the previous section we have the following script (copy and pasted for convenience): ```solidity contract ExampleCCABidScript is Script { function setUp() public {} function run() public { vm.startBroadcast(); ContinuousClearingAuction auction = ContinuousClearingAuction(vm.envAddress("AUCTION_ADDRESS")); uint256 maxPrice = auction.floorPrice() + auction.tickSpacing(); // Bid at the next possible price uint256 amountRequired = (maxPrice * uint256(auction.totalSupply())) >> 96; uint128 amount = uint128(amountRequired); address owner = vm.envAddress("DEPLOYER"); // The deployer is the owner of the bid by default uint256 bidId = auction.submitBid{value: amount}(maxPrice, amount, owner, bytes("")); console2.log("Bid submitted with ID:", bidId); vm.roll(block.number + 1); auction.checkpoint(); console2.log("checkpoint clearingPrice:", auction.clearingPrice()); vm.stopBroadcast(); } } ``` Let's add another bid to the auction after our initial one. We'll bid at a price higher than the first bid but unlike the first bid, we won't deposit enough ETH to move the clearing price of the auction up to this new price. The second bid will be large enough to move the clearing price of the auction up, outbidding the first bid, but not large enough to move the clearing price of the auction up to the second bid's max price. ```solidity function run() public { vm.startBroadcast(); ContinuousClearingAuction auction = ContinuousClearingAuction(vm.envAddress("AUCTION_ADDRESS")); uint256 maxPrice = auction.floorPrice() + auction.tickSpacing(); // Bid at the next possible price uint256 amountRequired = (maxPrice * uint256(auction.totalSupply())) >> 96; uint128 amount = uint128(amountRequired); address owner = vm.envAddress("DEPLOYER"); // The deployer is the owner of the bid by default uint256 bidId = auction.submitBid{value: amount}(maxPrice, amount, owner, bytes("")); console2.log("First bid submitted with ID:", bidId); vm.roll(block.number + 1); auction.checkpoint(); console2.log("checkpoint clearingPrice after first bid:", auction.clearingPrice()); maxPrice = auction.floorPrice() + 2 * auction.tickSpacing(); // Bid at a higher price than the first one amountRequired = (maxPrice * uint256(auction.totalSupply())) >> 96; // Deposit ~90% the amount of ETH required so the clearing price ends up somewhere between the first and second bid's max prices. amount = uint128(amountRequired * 9 / 10); bidId = auction.submitBid{value: amount}(maxPrice, amount, owner, bytes("")); console2.log("Second bid submitted with ID:", bidId); vm.roll(block.number + 1); auction.checkpoint(); console2.log("checkpoint clearingPrice after second bid:", auction.clearingPrice()); vm.stopBroadcast(); } } ``` You can run the script with the following command: ```bash AUCTION_ADDRESS= forge script scripts/ExampleCCABidScript.s.sol:ExampleCCABidScript \ --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast -vvvv --slow ``` You'll see the following logs in the console: ``` == Logs == First bid submitted with ID: 0 checkpoint clearingPrice after first bid: 158456325028528668016640 Second bid submitted with ID: 1 checkpoint clearingPrice after second bid: 215207282483414186944057 ``` Great! We've successfully submitted two bids to the auction and the clearing price of the auction is now at `215207282483414186944057`. ### Partially vs. fully filled bids Bids in the auction can have periods where they are partially and fully filled. At a high level, a bid is considered "partially filled" whenever the clearingPrice is equal to the bid's max price. When the clearing price is strictly lower than the bid's max price, the bid is considered "fully filled". Once the clearingPrice moves above the bid's max price, the bid is outbid and stops purchasing tokens. Let's take a look at the state of the two bids in the auction at this point in time: | Checkpoint | Bid 0 (max price: 1:1e6) | Bid 1 (max price: 1:2e6) | |----------------|------------------------------------|---------------------------| | Checkpoint 0 (79228162514264334008320) | Not placed | Not placed | | Checkpoint 1 (158456325028528668016640) | Partially filled | Not placed | | Checkpoint 2 (215207282483414186944057) | Outbid | Fully filled | The first bid is partially filled because the clearing price of the auction is equal to the bid's max price at checkpoint 1. The second bid is fully filled because the clearing price of the auction is strictly lower than the bid's max price at checkpoint 2 (215207282483414186944057 < 237684487542793002024960) or equivalently (2.7e-06 < 3e-06). As of checkpoint 2 the first bid is considered `outbid` and it can be exited from the auction. It only participated for one block and the remainder of its ETH can be refunded back to the `owner`. Let's append the following code to the end of the script to exit the first bid. ```solidity function run() public { // All the code from the previous script... uint64 lastCheckpointedBlock = auction.lastCheckpointedBlock(); uint256 ownerBalanceBefore = address(owner).balance; auction.exitPartiallyFilledBid(0, uint64(firstCheckpointBlock), lastCheckpointedBlock); console2.log("Bid 0 exited"); console2.log("Refunded ETH:", address(owner).balance - ownerBalanceBefore); Bid memory bid = auction.bids(0); console2.log("Bid 0 tokensFilled:", bid.tokensFilled); console2.log("Bid 0 exitedBlock:", bid.exitedBlock); } } ``` Running this script gives the following output: ``` == Logs == First bid submitted with ID: 0 checkpoint clearingPrice after first bid: 158456325028528668016640 Second bid submitted with ID: 1 checkpoint clearingPrice after second bid: 215641168133582360708057 Bid 0 exited Refunded ETH: 1995999999999999909677 Bid 0 tokensFilled: 2000000000000000000000000 Bid 0 exitedBlock: 5 ``` The first bid is exited successfully and the remainder of its ETH can be refunded back to the `owner`. It purchased `2000000000000000000000000` wei of tokens (2,000,000) and was exited at block `5`. As the auction has not completed yet, the second bid is not exitable yet since its maxPrice is higher than the clearing price of the auction. At the end of the auction and after `claimBlock` both bids can claim their purchased tokens. ### Claiming tokens A bid must be exited before claiming tokens. Let's fast forward to the end of the auction so we can exit the second bid and claim tokens for both bids. Append the following code to the end of the script: ```solidity function run() public { // All the code from the previous script... vm.roll(auction.endBlock()); ownerBalanceBefore = address(owner).balance; auction.exitBid(1); // Exit the second bid console2.log("Bid 1 exited"); console2.log("Refunded ETH:", address(owner).balance - ownerBalanceBefore); bid = auction.bids(1); console2.log("Bid 1 exitedBlock:", bid.exitedBlock); vm.roll(auction.claimBlock()); uint256 ownerTokenBalanceBefore = auction.token().balanceOf(owner); auction.claimTokens(0); uint256 ownerTokenBalanceAfter = auction.token().balanceOf(owner); console2.log("Bid 0 tokens claimed:", ownerTokenBalanceAfter - ownerTokenBalanceBefore, (ownerTokenBalanceAfter - ownerTokenBalanceBefore) / 1e18); ownerTokenBalanceBefore = auction.token().balanceOf(owner); auction.claimTokens(1); ownerTokenBalanceAfter = auction.token().balanceOf(owner); console2.log("Bid 1 tokens claimed:", ownerTokenBalanceAfter - ownerTokenBalanceBefore, (ownerTokenBalanceAfter - ownerTokenBalanceBefore) / 1e18); vm.stopBroadcast(); } } ``` Running this script gives the following output: ``` Bid 0 exited Refunded ETH: 1995999999999999909677 Bid 0 exitedBlock: 4 Bid 1 exited Refunded ETH: 0 Bid 1 exitedBlock: 100 Bid 0 tokens claimed: 2000000000000000000000000 2000000 Bid 1 tokens claimed: 993999999999999999999999459 993999999 ``` The second bid is exited successfully and it refunded 0 ETH back to the `owner`. This is expected since it was fully filled for the duration of the auction. The first bid purchased `2,000,000` tokens and the second bid purchased `993,999,999` tokens. Given the total supply of 1 billion tokens this makes sense since the second bid completely outbid the first bid after the first checkpoint. Note that these two values don't add up perfectly to the total supply since the auction was not fully subscribed at its `startBlock`. The first bid was entered a few blocks after the start of the auction and thus the token supply allocated to those "missed" blocks were not sold. These unsold tokens can be swept back to the preconfigured `tokensRecipient` by calling `sweepUnsoldTokens` after the end of the auction. ### Sweep unsold tokens At the end of the auction any unsold tokens can be swept back to the preconfigured `tokensRecipient` by calling `sweepUnsoldTokens`. This function is permissionless and can be called by anyone. ```solidity function run() public { // All the code from the previous script... uint256 tokensRecipientBalanceBefore = auction.token().balanceOf(auction.tokensRecipient()); auction.sweepUnsoldTokens(); uint256 tokensRecipientBalanceAfter = auction.token().balanceOf(auction.tokensRecipient()); console2.log("Unsold tokens swept:", tokensRecipientBalanceAfter - tokensRecipientBalanceBefore, (tokensRecipientBalanceAfter - tokensRecipientBalanceBefore) / 1e18); vm.stopBroadcast(); } ``` Running this script gives the following output: ``` Unsold tokens swept: 4000000000000000000000540 4000000 ``` We can see that `4,000,000` tokens were not sold in the auction and were swept back to the `tokensRecipient`. Combining this value with the total number of tokens purchased by the first bid and the second bid gives us the total supply of tokens sold in the auction (2,000,000 + 993,999,999 + 4,000,000 = 1,000,000,000). ### Sweep raised currency At the end of the auction any raised currency can be swept back to the preconfigured `currencyRecipient` by calling `sweepCurrency`. This function is also permissionless and can be called by anyone. ```solidity function run() public { // All the code from the previous script... uint256 fundsRecipientBalanceBefore = address(auction.fundsRecipient()).balance; auction.sweepCurrency(); uint256 fundsRecipientBalanceAfter = address(auction.fundsRecipient()).balance; console2.log("Currency swept:", fundsRecipientBalanceAfter - fundsRecipientBalanceBefore, (fundsRecipientBalanceAfter - fundsRecipientBalanceBefore) / 1e18); } ``` Running this script gives the following output: ``` Currency swept: 2703999999999999877637 2703 ``` We can see that `2703999999999999877637` wei of currency (~2,703 ETH) was raised in the auction and was swept to the preconfigured `fundsRecipient`. ## Next steps This concludes the quickstart guide for the CCA auction mechanism! We covered: - How to setup a local development environment - How to configure a CCA auction - Submitting a bid and understanding price discovery - Exiting a bid and claiming tokens - Sweeping unsold tokens and the total raised currency For more details please refer to the [technical reference](/docs/contracts/liquidity-launchpad/05-auction-mechanism.md). Additionally, the contracts are open sourced and MIT licensed. You can find the source code for the contracts in the [GitHub repository](https://github.com/Uniswap/continuous-clearing-auction). --- ## Submitting a bid This section will walk you through submitting a bid on a CCA auction. ## Prerequisites Basic understanding of the CCA auction mechanism and Solidity is assumed. This guide continues from the [previous section](/docs/contracts/liquidity-launchpad/quickstart/example-configuration.md) where we configured a CCA auction and deployed it to our local anvil node. ## Summary Currently we have a CCA contract deployed which we can interact with. It has the following parameters: | Parameter | Value | Notes | |-----------------------|--------------------------------------------|----------------------------------------------------------------------------------------------------------------| | currency | `address(0)` | We'll use the native token for this example | | tokensRecipient | `deployer` | Address that will receive leftover tokens | | fundsRecipient | `deployer` | Address that will receive the funds raised during the auction | | startBlock | `uint64(block.number)` | Start the auction on the current block | | endBlock | `uint64(block.number + 100)` | End the auction after 100 blocks | | claimBlock | `uint64(block.number + 100)` | Allow claims at the end of the auction | | tickSpacing | `79228162514264334008320` | Use a tick spacing equal to the floor price | | validationHook | `address(0)` | Use no validation hook | | floorPrice | `79228162514264334008320` | Use a floor price representing a ratio of 1:1,000,000 | | requiredCurrencyRaised| `0` | No graduation threshold | We also added a token supply schedule of 10% in the first 50 blocks, 49% in the next 49 blocks, and 41% in the last block. ## Interacting with the auction contract Let's create a new script to interact with the deployed auction contract. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract ExampleCCABidScript is Script { function setUp() public {} function run() public { vm.startBroadcast(); ContinuousClearingAuction auction = ContinuousClearingAuction(vm.envAddress("AUCTION_ADDRESS")); vm.stopBroadcast(); } } ``` You can run this with the following command: ```bash AUCTION_ADDRESS= forge script scripts/ExampleCCABidScript.s.sol:ExampleCCABidScript \ --rpc-url http://localhost:8545 --private-key ``` ## Submitting a bid To submit a bid, we need to call the `submitBid()` function on the auction contract. The function has the following signature: ```solidity function submitBid(uint256 maxPrice, uint128 amount, address owner, uint256 prevTickPrice, bytes calldata hookData) external payable returns (uint256 bidId); ``` The `maxPrice` is the maximum price the bidder is willing to pay. This MUST be strictly above the current clearing price. The `amount` is the amount of currency the user is bidding, and `owner` is the address of the user who will receive any purchased tokens or refunded currency. The `prevTickPrice` is the price of the tick immediately preceding the tick to insert the bid into. You can also omit this and the contract will iterate through every tick starting from the floor price until it reaches the correct position. The `hookData` is the data to pass to the validation hook. We'll omit this since we're not using a validation hook. Let's add this to our script: ```solidity function run() public { vm.startBroadcast(); ContinuousClearingAuction auction = ContinuousClearingAuction(vm.envAddress("AUCTION_ADDRESS")); uint256 maxPrice = auction.floorPrice() + auction.tickSpacing(); // Bid at the next possible price uint128 amount = 1 ether; address owner = vm.envAddress("DEPLOYER"); // The deployer is the owner of the bid by default uint256 bidId = auction.submitBid{value: amount}(maxPrice, amount, owner, bytes("")); console2.log("Bid submitted with ID:", bidId); vm.stopBroadcast(); } ``` You can use the public anvil private key (`0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`) when running the script as it has an existing balance of ETH: ```bash AUCTION_ADDRESS= forge script scripts/ExampleCCABidScript.s.sol:ExampleCCABidScript \ --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -vvvv ``` The output should look like this: ```bash Bid submitted with ID: 0 ``` Congratulations! You've submitted your first bid on a CCA auction. ### Breaking down the events If you look through the logs you'll see quite a few events emitted by the auction contract. ``` ├─ emit ClearingPriceUpdated(blockNumber: 3, clearingPrice: 79228162514264334008320 [7.922e22]) ├─ emit CheckpointUpdated(blockNumber: 3, clearingPrice: 79228162514264334008320 [7.922e22], cumulativeMps: 60000 [6e4]) ├─ emit NextActiveTickUpdated(price: 158456325028528668016640 [1.584e23]) ├─ emit TickInitialized(price: 158456325028528668016640 [1.584e23]) ├─ emit BidSubmitted(id: 0, owner: RIPEMD-160: [0x0000000000000000000000000000000000000003], price: 158456325028528668016640 [1.584e23], amount: 1999999999999999909496 [1.999e21]) ``` Clearly a lot more is happening that just submitting a bid. The first thing we see happening is the auction creating a new checkpoint for the current block (in this case, block 3). At this point in time the auction has already started (remember how we used `block.number` for the start block?) and you can see that the `cumulativeMps` field, which tracks the percentage of auction which has passed, is at 60_000, or 6%. We also see that the auction updates the clearing price to the floor price (`79228162514264334008320`). You can verify that this is equal to the expected ratio of 1 ETH to 1 million tokens: ```python >>> 79228162514264334008320 / 2**96 1e-06 ``` Additionally we see the auction initializing a new tick at the price which we placed our bid at (`158456325028528668016640`). This value is equal to `floorPrice + tickSpacing` as expected (for simplicity we set tickSpacing equal to the floor price so this is 2 * floorPrice). Finally we see the `BidSubmitted` event, which contains the bid ID, the owner of the bid, the price at which the bid was placed, and the amount of currency that was bid. ## Next steps In the next section we'll modify our script to show how the price of the auction can change over time. We'll also cover exiting and claiming the tokens from a bid. --- ## Create a local deployment This guide will walk you through deploying a Continuous Clearing Auction (CCA) instance locally. ## Prerequisites - [Foundry](https://getfoundry.sh/introduction/installation) - [Anvil](https://getfoundry.sh/anvil/overview) Also, please check out the [setup guide](./setup.md) for installation instructions. ## Deployment methods There are two main ways to deploy new CCA auctions: 1. Via the `ContinuousClearingAuctionFactory` contract (recommended) 2. Deploying a new `ContinuousClearingAuction` contract directly ### Deploying via factory (recommended) The `ContinuousClearingAuctionFactory` contract is a factory contract that deploys new instances of the contract. It inherits the `IDistributionStrategy` interface from the [Liquidity Launcher](https://github.com/Uniswap/liquidity-launcher) contracts. ```solidity interface IDistributionStrategy { /// @notice Initialize a distribution of tokens under this strategy. /// @dev Contracts can choose to deploy an instance with a factory-model or handle all distributions within the /// implementing contract. For some strategies this function will handle the entire distribution, for others it /// could merely set up initial state and provide additional entrypoints to handle the distribution logic. /// @param token The token that is being distributed. /// @param totalSupply The supply of the token that is being distributed. /// @param configData Arbitrary, strategy-specific parameters. /// @param salt The optional salt for deterministic deployment. /// @return distributionContract The contract that will handle or manage the distribution. /// (Could be `address(this)` if the strategy is handled in-place, or a newly deployed instance). function initializeDistribution(address token, uint256 totalSupply, bytes calldata configData, bytes32 salt) external returns (IDistributionContract distributionContract); } ``` The factory contract is deployed to the same address across the following networks: | Network | Address | Commit Hash | Version | | -------- | ------------------------------------------ | ---------------------------------------- | ---------------- | | Mainnet | 0xcca1101C61cF5cb44C968947985300DF945C3565 | 95d7da7a2d25cf60f14eaccd6ab5fb24d393a452 | v1.1.0 | | Unichain | 0xcca1101C61cF5cb44C968947985300DF945C3565 | 95d7da7a2d25cf60f14eaccd6ab5fb24d393a452 | v1.1.0 | | Base | 0xcca1101C61cF5cb44C968947985300DF945C3565 | 95d7da7a2d25cf60f14eaccd6ab5fb24d393a452 | v1.1.0 | | Sepolia | 0xcca1101C61cF5cb44C968947985300DF945C3565 | 95d7da7a2d25cf60f14eaccd6ab5fb24d393a452 | v1.1.0 | See the [Deployments Guide](https://github.com/Uniswap/continuous-clearing-auction/blob/main/docs/DeploymentGuide.md) for more details on deploying the factory contract to other networks. ### Deploying a new `ContinuousClearingAuction` contract directly CCA auctions can be deployed directly by calling the constructor: ```solidity constructor(address _token, uint128 _totalSupply, AuctionParameters memory _parameters) /// from: https://github.com/Uniswap/continuous-clearing-auction/blob/main/src/interfaces/IContinuousClearingAuction.sol struct AuctionParameters { address currency; // token to raise funds in. Use address(0) for ETH address tokensRecipient; // address to receive leftover tokens address fundsRecipient; // address to receive all raised funds uint64 startBlock; // Block which the first step starts uint64 endBlock; // When the auction finishes uint64 claimBlock; // Block when the auction can claimed uint256 tickSpacing; // Fixed granularity for prices address validationHook; // Optional hook called before a bid uint256 floorPrice; // Starting floor price for the auction uint128 requiredCurrencyRaised; // Amount of currency required to be raised for the auction to graduate bytes auctionStepsData; // Packed bytes describing token issuance schedule } ``` We'll be using the factory method for the rest of this guide. ## ContinuousClearingAuctionFactory The factory contract has two functions: `initializeDistribution()` and `getAuctionAddress()`. It has no constructor parameters so it can be easily deployed to the same address across all networks. ### initializeDistribution() This is the main entrypoint and is called by integrating contracts within the `LiquidityLauncher` system. It performs basic validation checks: - Requires the amount of tokens to be less than `uint128.max` - CREATE2 prevents the same auction configuration from being initialized twice To avoid issues with circular deployments, the factory contract will replace the `tokenRecipient` and `fundsRecipient` parameters if they are set to the sentinel "MSG_SENDER" address (address(1)). ```solidity // If the tokensRecipient is address(1), set it to the msg.sender if (parameters.tokensRecipient == ActionConstants.MSG_SENDER) parameters.tokensRecipient = msg.sender; // If the fundsRecipient is address(1), set it to the msg.sender if (parameters.fundsRecipient == ActionConstants.MSG_SENDER) parameters.fundsRecipient = msg.sender; ``` Finally, an optional caller provided `salt` is hashed with the auction parameters to allow for deterministic deployments and vanity addresses. This function emits the `AuctionCreated` event with address of the new auction contract as well as the `amount` and `token` being distributed. ### getAuctionAddress() This function is a simple view function that performs the same logic as `initializeDistribution()` but does not deploy a new contract. It calculates and returns the address of the new auction contract. Onchain integrators will find this function useful for calculating the address of an auction contract before it is deployed. ## Starting our local deployment script Let's start by creating a new script file in the `scripts` folder. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract ExampleCCADeploymentScript is Script { function setUp() public {} function run() public { vm.startBroadcast(); ContinuousClearingAuctionFactory factory = new ContinuousClearingAuctionFactory(); console2.log("Factory deployed to:", address(factory)); // TODO: configure the auction and deploy it via `initializeDistribution()` vm.stopBroadcast(); } } ``` Then, you can run the script: ```bash forge script scripts/ExampleCCADeploymentScript.s.sol:ExampleCCADeploymentScript \ --rpc-url http://localhost:8545 --private-key --broadcast ``` This will deploy the factory contract. ### Next steps In the next section we'll walk through the different parameters of a CCA auction and create an example configuration. --- ## Price discovery # Entering price discovery This section will walk you through how a CCA auction discovers new prices over time. ## Prerequisites Basic understanding of the CCA auction mechanism and Solidity is assumed. This guide continues from the [previous section](/docs/contracts/liquidity-launchpad/quickstart/first-bid.md) where we submitted our first bid. ## Summary Currently we have a CCA contract deployed which we have submitted a bid to. We'll now modify our script to show how the price of the auction can change over time. To summarize the current relative parameters: | Parameter | Value | Notes | |-----------------------|--------------------------------------------|----------------------------------------------------------------------------------------------------------------| | currency | `address(0)` | We'll use the native token for this example | | tickSpacing | `79228162514264334008320` | Use a tick spacing equal to the floor price | | floorPrice | `79228162514264334008320` | Use a floor price representing a ratio of 1:1,000,000 | And the total supply to sell is 1 billion tokens. ## Discovering a new price In the last section we showed how the auction updates its internal state via checkpointing when a new bid is submitted. The bid that we submitted had two main parts: a max price and an amount. The [whitepaper](https://uniswap.org/whitepaper.pdf) is the best resource to understand the mechanics of the auction but at a high level this is how the auction's mechanism works: - Given that each bid is willing to purchase tokens until some _maximum price_, there exists a price for which no one is willing to participate in the auction. - A less extreme version of the idea above is that there exists a _clearing price_ for which all of the demand (ex. ETH) in the auction can purchase all of the tokens that are being sold. - This equilibrium can only change if a new bid is submitted into the auction. - Thus, every time a new bid is added we try to find a new _clearing price_. We do this by iterating through each price tick until we find a price at which there is not enough demand to purchase all of the tokens that are being sold. - This is the _clearing price_ and it is the price at which all active participants will pay in this block. Hopefully you can quickly reason about how much `currency` is required to move the price of the auction above the floor: `required currency = desired price * totalSupply` In our example, the floor price is a 1:1,000,000 ratio and the total supply is 1 billion tokens. So we'll need 1,000 ETH to move the price of the auction above the floor. Let's modify our script to submit enough ETH to move the price of the auction above the floor. ```solidity contract ExampleCCABidScript is Script { function setUp() public {} function run() public { vm.startBroadcast(); ContinuousClearingAuction auction = ContinuousClearingAuction(vm.envAddress("AUCTION_ADDRESS")); uint256 maxPrice = auction.floorPrice() + auction.tickSpacing(); // Bid at the next possible price uint256 amountRequired = (maxPrice * uint256(auction.totalSupply())) >> 96; uint128 amount = uint128(amountRequired); address owner = vm.envAddress("DEPLOYER"); // The deployer is the owner of the bid by default uint256 bidId = auction.submitBid{value: amount}(maxPrice, amount, owner, bytes("")); console2.log("Bid submitted with ID:", bidId); vm.roll(block.number + 1); auction.checkpoint(); console2.log("checkpoint clearingPrice:", auction.clearingPrice()); vm.stopBroadcast(); } } ``` You can run the script with the following command (the --slow is important!) ```bash AUCTION_ADDRESS= forge script scripts/ExampleCCABidScript.s.sol:ExampleCCABidScript \ --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast -vvvv --slow ``` The output should look like this: ``` [335278] 0x1fE7eaE06ef5c6b5f7eeA526Ca1fc1945153dF58::submitBid{value: 1999999999999999909496}(158456325028528668016640 [1.584e23], 1999999999999999909496 [1.999e21], RIPEMD-160: [0x0000000000000000000000000000000000000003], 0x) ├─ emit ClearingPriceUpdated(blockNumber: 3, clearingPrice: 79228162514264334008320 [7.922e22]) ├─ emit CheckpointUpdated(blockNumber: 3, clearingPrice: 79228162514264334008320 [7.922e22], cumulativeMps: 60000 [6e4]) ├─ emit NextActiveTickUpdated(price: 158456325028528668016640 [1.584e23]) ├─ emit TickInitialized(price: 158456325028528668016640 [1.584e23]) ├─ emit BidSubmitted(id: 0, owner: RIPEMD-160: [0x0000000000000000000000000000000000000003], price: 158456325028528668016640 [1.584e23], amount: 1999999999999999909496 [1.999e21]) └─ ← [Return] 0 [177725] 0x1fE7eaE06ef5c6b5f7eeA526Ca1fc1945153dF58::checkpoint() ├─ emit NextActiveTickUpdated(price: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) ├─ emit ClearingPriceUpdated(blockNumber: 4, clearingPrice: 158456325028528668016640 [1.584e23]) ├─ emit CheckpointUpdated(blockNumber: 4, clearingPrice: 158456325028528668016640 [1.584e23], cumulativeMps: 80000 [8e4]) └─ ← [Return] Checkpoint({ clearingPrice: 158456325028528668016640 [1.584e23], currencyRaisedAtClearingPriceQ96_X7: 3169126500570573360332800000000000000000000000000000000 [3.169e54], cumulativeMpsPerPrice: 5545971375998503882513753047040011356670 [5.545e39], cumulativeMps: 80000 [8e4], prev: 3, next: 18446744073709551615 [1.844e19] }) ``` Observe that we bid `1999999999999999909496` wei of ETH, which is approximately 2,000 ETH, and the auction's clearing price moved to `158456325028528668016640`. This is equal to our max price of `158456325028528668016640`. As such, we have successfully moved the price of the auction above the floor to exactly our maxPrice. This is because our bid deposited exactly the amount of ETH required to purchase the `totalSupply` of tokens at the given price. You can see in the forge script how the amount of ETH is calculated: `uint256 amountRequired = (maxPrice * uint256(auction.totalSupply())) >> 96`. Given the total supply of 1 billion tokens (1e9), it will require 2000 (2e3) ETH to move the price to a ratio of 1:2,000,000 (1:2e6). This follows our intuition from the previous section, where the auction finds the highest price for which there is enough demand willing to purchase the `totalSupply` of tokens. If the clearing price moved above our max price, we would be outbid, and as we are the only bidder in the auction, there would be no demand to match with the supply. That's why the price of the auction moved exactly to the max price of the bid. We also see that the new Checkpoint created in block #4 has the updated clearing price. ## Next steps In the next section we'll cover how a bid can be exited and how tokens can be claimed. --- ## Setup # Getting started with CCA **Repository:** [github.com/Uniswap/continuous-clearing-auction](https://github.com/Uniswap/continuous-clearing-auction) ## Clone the repo ```bash git clone https://github.com/Uniswap/continuous-clearing-auction.git cd continuous-clearing-auction ``` ## Quickstart Currently, developing with CCA locally _requires [foundry](https://getfoundry.sh/)_ ```bash curl -L https://foundry.paradigm.xyz | bash ``` Install dependencies ```bash forge install ``` Build the contracts ```bash forge build ``` Install pre-commit hooks (optional) ```bash pre-commit install pre-commit run --all-files ``` Remappings Remappings are already set up in `foundry.toml` file but you may need to adjust if importing CCA into an existing foundry project. Start an anvil node on localhost:8545 ```bash anvil ``` --- ## Deployments(Protocol-fee) ## Ethereum Mainnet | Contract | Address | |----------|---------| | MainnetDeployer | [0xd3Aa12B99892b7D95BBAA27AEf222A8E2a038C0C](https://etherscan.io/address/0xd3Aa12B99892b7D95BBAA27AEf222A8E2a038C0C) | | TokenJar (AssetSink) | [0xf38521f130fcCF29dB1961597bc5d2B60F995f85](https://etherscan.io/address/0xf38521f130fcCF29dB1961597bc5d2B60F995f85) | | Releaser (Firepit) | [0x0D5Cd355e2aBEB8fb1552F56c965B867346d6721](https://etherscan.io/address/0x0D5Cd355e2aBEB8fb1552F56c965B867346d6721) | | V3FeeAdapter | [0x5E74C9f42EEd283bFf3744fBD1889d398d40867d](https://etherscan.io/address/0x5E74C9f42EEd283bFf3744fBD1889d398d40867d) | | UNI | [0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984](https://etherscan.io/address/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984) | ## Unichain | Contract | Address | |----------|---------| | UnichainDeployer | [0xD16c47bf3ae22e0B2BAc5925D990b81416f18dea](https://uniscan.xyz/address/0xD16c47bf3ae22e0B2BAc5925D990b81416f18dea) | | TokenJar | [0xD576BDF6b560079a4c204f7644e556DbB19140b5](https://uniscan.xyz/address/0xD576BDF6b560079a4c204f7644e556DbB19140b5) | | Releaser (OptimismBridgedResourceFirepit) | [0xe0A780E9105aC10Ee304448224Eb4A2b11A77eeB](https://uniscan.xyz/address/0xe0A780E9105aC10Ee304448224Eb4A2b11A77eeB) | ### Deployment Process To enable fees for v2 and v3 on *mainnet*, the [Deployer](/technical-reference/Deployer) handles the entire set up in one transaction. Subsequent transactions will be required to set the initial fee values. The `Deployer` contract performs the following steps, in its constructor: 1. Deploys the `AssetSink` contract 2. Deploys the `Firepit` contract, with the initial UNI-threshold requirement as a parameter 3. Set the `Firepit` as the releaser on the `AssetSink` 4. Transfers ownership of the `AssetSink` to the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc) 5. Sets the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc) as the initial `thresholdSetter`, giving the Governance Timelock the ability to update the UNI-threshold requirement 6. Transfers ownership of the `Firepit` to the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc). Uniswap Governance can appoint a different `thresholdSetter` at a later time. 7. Deploys the `V3FeeController` contract, with the `AssetSink` as the destination for collected fees 8. Sets the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc) as the initial `feeSetter`, giving the Governance Timelock the ability to set fee values at a later date 9. Transfers ownership of the `V3FeeController` to the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc), giving the Governance Timelock the ability to appoint a different `feeSetter` at a later time Enabling fee values themselves will require a Uniswap Governance vote, which will involve: - calling [`setFeeTo`](https://github.com/Uniswap/v2-core/blob/ee547b17853e71ed4e0101ccfd52e70d5acded58/contracts/UniswapV2Factory.sol#L40) on the mainnet [UniswapV2Factory](https://etherscan.io/address/0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f) and passing the `AssetSink` address as the parameter - calling [`setMerkleRoot`](https://github.com/Uniswap/protocol-fees/blob/6f39580d36a75a1ed1f34daf7d3262ced4b9df3f/src/feeControllers/V3FeeController.sol#L93C12-L93C25) on the V3FeeController contract and passing a properly constructed Merkle root as a parameter --- ## Fee Configuration # **Protocol Fee Configuration** For v2, fees are hardcoded and can only be enabled or disabled across all pools at once. A full list of v3 pools included in the initial rollout is available [here](https://github.com/Uniswap/protocol-fees/blob/main/merkle-generator/data/merkle-tree.json). | Version | Fee Tier | LP Fee | Protocol Fee | | ----- | ----- | ----- | ----- | | v2 | All pools | 0.25% | 0.05% | | v3 | 0.01% | 0.0075% | 0.0025% | | v3 | 0.05% | 0.0375% | 0.0125% | | v3 | 0.30% | 0.25% | 0.05% | | v3 | 1.00% | 0.8334% | 0.1666% | --- ## Best Practices Below, please find a list of best practices when interacting with the Uniswap Protocol Fee system. ## Calldata Nonce Recall [`Firepit.release()`](../technical-reference/IReleaser#release) requires a `_nonce` parameter that matches the contract storage [`Firepit.nonce()`](../technical-reference/INonce#nonce). The nonce mechanism prevents front-running attacks, where a successful front-run would cause the victim to burn a substantial amount of UNI in exchange for little to no assets. The nonce mechanism ensures sequencing, so **if two competing transactions use the same nonce, only one will succeed** :::danger Integrating *contracts* should **NEVER** read the `nonce` at the time of the transaction ```solidity contract Vulnerable { IFirepit public firepit; function vulnerable(Currency[] memory assets) external { // ❌ THIS IS VULNERABLE TO FRONT RUNNING ❌ // uint256 nonce = firepit.nonce(); firepit.release(nonce, assets, address(this)); } } ``` ::: :::tip ```solidity contract SafeRelease { IFirepit public firepit; // ✅ THIS IS SAFE FROM FRONT RUNNING ✅ // function safeRelease(uint256 _nonce, Currency[] memory assets) external { firepit.release(_nonce, assets, address(this)); } } ``` In the safe pattern, the caller is responsible for reading the `Firepit.nonce()` offchain, assocating it with releasable assets, and passing it as a **calldata** parameter ::: ## Collect Uniswap v3 Fees For further reading, please see [Read Asset Balance](./read-asset-balance) In the Uniswap Protocol Fee system, Uniswap v3 fees *must be collected* to the `AssetSink` before they are eligible for release. :::tip We recommend that integrators check and collect Uniswap v3 fees before calling `Firepit.release()`, to ensure the maximum assets are released. Collection is available via [IV3FeeController.collect()](../technical-reference/IV3FeeController#collect) ```solidity IV3FeeController v3FeeController; function collectAndRelease( IV3FeeController.CollectParams memory collectParams, uint256 _nonce, Currency[] memory assets, address recipient ) external { v3FeeController.collect(collectParams); firepit.release(_nonce, assets, recipient); } ``` ::: ## UNI Approvals The Uniswap Protocol Fee system allows for an *updatable* [`Firepit.threshold()`](../technical-reference/IResourceManager#threshold) While the system does not intend to maliciously front-run `.release()` calls, max-approving UNI allowances may lead to an unexpectedly higher burn of UNI. The risk only appears if and only if the `thresholdSetter` increases the `threshold` amount while there is a pending `.release()` call. Integrators can avoid this risk by: * only holding an amount of UNI they are willing to burn * approving only the amount of UNI they intend to burn * perform balance checks before and after calling `.release()`, comparing the burned amount against a calldata value ## Payable Contracts Because the Uniswap Protocol Fee system can release native tokens (Ether), recipients of the tokens should be `payable` ## Pricing Uniswap v2 ERC-20 Tokens For further reading, please see [Read Asset Balance](./read-asset-balance#uniswap-v2-pool-protocol-fees) Uniswap v2 Protocol Fees are automatically pushed to the `AssetSink` contract, but are represented as ERC-20 LP tokens. These ERC-20 LP tokens represent a combination of `token0` and `token1` To compute the underlying amount of `token0` and `token1` represented by the LP tokens, integrators can use the following formula: ```solidity IUniswapV2Pair pair; function getUnderlyingAmounts(uint256 lpTokens) external view returns (uint256 amount0, uint256 amount1) { uint256 totalSupply = pair.totalSupply(); uint256 poolBalance0 = IERC20(pair.token0()).balanceOf(address(pair)); uint256 poolBalance1 = IERC20(pair.token1()).balanceOf(address(pair)); amount0 = (lpTokens * poolBalance0) / totalSupply; amount1 = (lpTokens * poolBalance1) / totalSupply; } ``` :::note Integrators should perform additional validation, i.e. pool price and slippage checks as to not misrepresent the LP token's underlying token amounts ::: --- ## Get Started In the current version of the Uniswap Protocol Fee system, searchers can *permissionlessly* capture value from the system by receiving assets valued greater than the UNI tokens provided to the system. The simplest way to interact with the system is calling [`Firepit.release()`](../technical-reference/IReleaser#release) from a wallet :::note It is possible to interact with the `Firepit` contract via a custom smart contract to enable: * slippage / balance checks * Uniswap v3 Fee [collection](./best-practices.mdx#collect-uniswap-v3-fees) * Uniswap v2 LP Token [redemptions](./read-asset-balance.mdx#uniswap-v2-pool-protocol-fees) * UNI token flash loans ::: ## Acquire a sufficient amount of UNI The `Firepit` contract requires integrators to hold a minimum amount of UNI to call `release()`. Participants can view the `threshold` by calling [`Firepit.threshold()`](../technical-reference/IResourceManager#threshold) ```solidity uint256 threshold = IFirepit(address(0x0000..TBD)).threshold(); ``` ```bash cast call 0x0000..TBD "threshold()(uint256)" --rpc-url ``` ## Approve the Firepit to spend UNI Because the `Firepit` contract transfers UNI to `address(0xdead)`, integrating addresses **must first approve** the contract to spend their UNI :::warning Integrators should assess the risks of max approving the `Firepit` contract ::: ```solidity IERC20(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984).approve( 0x0000..TBD, type(uint256).max ); ``` ```bash cast send 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 \ "approve(address,uint256)(bool)" \ --rpc-url \ 0x0000..TBD \ 115792089237316195423570985008687907853269984665640564039457584007913129639935 ``` ## Read the Nonce The `Firepit` contract uses a `nonce` as a safety mechanism to avoid malicious front-running. The value is provided to `release(...)` must be equal to the value in contract storage ```solidity [solidity] uint256 nonce = IFirepit(address(0x0000..TBD)).nonce(); ``` ```bash cast call 0x0000..TBD "nonce()(uint256)" --rpc-url ``` ## Call `Firepit.release()` Once the value of the assets exceeds the value of the UNI tokens, integrators should call [`Firepit.release()`](../technical-reference/IReleaser#release) ```solidity uint256 _nonce = IFirepit(address(0x0000..TBD)).nonce(); Currency[] memory _assets = new Currency[](3); _assets[0] = Currency.wrap(address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)); // USDC _assets[1] = Currency.wrap(address(0xdAC17F958D2ee523a2206206994597C13D831ec7)); // USDT _assets[2] = Currency.wrap(address(0x0000000000000000000000000000000000000000)); // ETH IFirepit(address(0x0000..TBD)).release(_nonce, _assets, 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); ``` ```bash cast send 0x0000..TBD "release(uint256,address[],address)" --rpc-url \ # example: release USDC, USDT, and ETH to vitalik.eth cast send 0x0000..TBD "release(uint256,address[],address)" --rpc-url \ 0 \ 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,0xdAC17F958D2ee523a2206206994597C13D831ec7,0x0000000000000000000000000000000000000000 \ 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 ``` --- ## Reading Asset Balance As noted in [Best Practices](./best-practices), the `AssetSink's` available asset balance is observed in three places: * the contract address of the `AssetSink` itself * the `UniswapV3Pool` contract addresses * the `UniswapV2Pair` liquidity tokens For example, the *releasable* USDC balance is `USDC.balanceOf(address(assetSink)) + sum of UniswapV3Pool.protocolFees() + sum of UniswapV2Pair.burn()` for each USDC-paired pool --- ## Uniswap v3 Pool Protocol Fees Reading available Uniswap v3 Pool protocol fees requires calling `.protocolFees()` on each `UniswapV3Pool` contract ```solidity contract Example { IAssetSink assetSink = IAssetSink(0x0); function releaseableBalance(address token, address[] calldata pools) external view returns (uint256) { return IERC20(token).balanceOf(address(assetSink)) // [!code hl] + _getV3ProtocolFeesUnclaimed(token, pools); // [!code hl] } function _getV3ProtocolFeesUnclaimed(address token, address[] calldata pools) internal view returns (uint256 feesUnclaimed) { uint128 token0Unclaimed; uint128 token1Unclaimed; for (uint256 i; i < pools.length; i++) { (token0Unclaimed, token1Unclaimed) = IUniswapV3Pool(pools[i]).protocolFees(); // [!code hl] // determine if the requested `token` parameter is `pool.token0()` or `pool.token1()` feesUnclaimed += pool.token0() == token ? token0Unclaimed : pool.token1() == token ? token1Unclaimed : 0; } } } ``` :::note Uniswap v3 does not support native Ether tokens. Protocol fees in ETH are accrued as [Wrapped Ether (WETH)](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2). ::: Because unclaimed tokens are stored in Uniswap v3 Pool contracts, integrators should use offchain indexing to track which pools contain the asset of interest. For example, USDC protocol fees are accrued in many different pools: * USDC / TOKENA 0.05% Fee * USDC / TOKENA 0.30% Fee * USDC / TOKENB 0.30% Fee * USDC / TOKENC 0.30% Fee * and so on... To track which pools contain the asset of interest, we recommend to index the `PoolCreated` [event emitted](https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Factory.sol#L50) by the `UniswapV3Factory` contract ## Uniswap v2 Pool Protocol Fees Uniswap v2 protocol fees are automatically "pushed" to the `AssetSink` so no additional calls are required to make the assets releasable. However, Uniswap v2 protocol fees are accrued in the form of liquidity tokens (LP tokens) which are redeemable for underlying assets. Ownership of the LP token represents the proportional share of the pool's assets. ``` (LP Token Balance / LP Token Total Supply) * Pool Reserves ``` ```solidity IERC20 lpToken; uint256 amount0 = (lpToken.balanceOf(address(assetSink)) * IERC20(lpToken.token0()).balanceOf(address(lpToken))) / lpToken.totalSupply(); uint256 amount1 = (lpToken.balanceOf(address(assetSink)) * IERC20(lpToken.token1()).balanceOf(address(lpToken))) / lpToken.totalSupply(); ``` :::note To access the underlying assets, integrators should call [IUniswapV2Pair.burn()](https://github.com/Uniswap/v2-core/blob/ee547b17853e71ed4e0101ccfd52e70d5acded58/contracts/UniswapV2Pair.sol#L134) ```solidity (uint256 amount0, uint256 amount1) = IUniswapV2Pair(pool).burn(recipient); ``` ::: --- ## Overview(Protocol-fee) # Uniswap Protocol Fees Fees from protocol versions (v2, v3, v4, UniswapX, Unichain) flow into a single onchain collector on each network. From there, specialized contracts handle how those assets are released or transformed (for example, burning UNI). ## Flow of Assets 1. **Fee Sources** Each protocol component that generates fees (v2, v3, v4, UniswapX, Unichain, and others) routes those fees through an associated Fee Adapter contract. Adapters define how and when fees are collected and send them to the TokenJar on the same chain. 2. **TokenJar** Each chain has one immutable contract called the **TokenJar**. All fee sources send tokens here. It’s the unified collection point for raw tokens. 3. **Releasers** Releasers define how tokens in the TokenJar are accessed. The simplest version burns UNI in exchange for the collected tokens. Other Releasers could handle cross-chain conversions or alternative value flows. 4. **Governance Control** Governance can update which Releaser a TokenJar uses or adjust configuration on Fee Adapters, but the core contracts themselves are immutable. ## Components ### TokenJar Immutable contract that receives all tokens from fee sources on a given chain. Only the active Releaser can withdraw from it. See [TokenJar](technical-reference/AssetSink.md) for more. ### Fee Adapters Adapters that connect specific protocol versions or products to the TokenJar. They define how and when fees are collected. See [Fee Adapters](technical-reference/V3FeeController.md). ### Releasers Contracts that convert collected fees into protocol value according to defined logic. Examples include the Firepit Releaser, which burns UNI in return for assets. See [Releasers](technical-reference/IReleaser.md). ## Real-World Example - Assume 100 UNI is the `threshold` amount for the Firepit Releaser. - When the top *N* assets in the *TokenJar* are worth slightly more than 100 UNI, **anyone** is incentivized to use the `Firepit.release()` contract to: 1. Burn 100 UNI 2. Withdraw the top *N* assets, *valued* at slightly more than 100 UNI --- ## ArrayLib [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/libraries/ArrayLib.sol) ## Functions ### includes ```solidity function includes(uint24[] storage array, uint24 value) internal view returns (bool); ``` --- ## Deployer [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/Deployer.sol) ## State Variables ### TOKEN_JAR ```solidity ITokenJar public immutable TOKEN_JAR ``` ### RELEASER ```solidity IReleaser public immutable RELEASER ``` ### FEE_ADAPTER ```solidity IV3FeeAdapter public immutable FEE_ADAPTER ``` ### UNI_MINTER ```solidity IUNIMinter public immutable UNI_MINTER ``` ### RESOURCE ```solidity address public constant RESOURCE = 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 ``` ### THRESHOLD ```solidity uint256 public constant THRESHOLD = 69_420 ``` ### V3_FACTORY ```solidity IUniswapV3Factory public constant V3_FACTORY = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984) ``` ### SALT_TOKEN_JAR ```solidity bytes32 constant SALT_TOKEN_JAR = 0 ``` ### SALT_RELEASER ```solidity bytes32 constant SALT_RELEASER = 0 ``` ### SALT_FEE_ADAPTER ```solidity bytes32 constant SALT_FEE_ADAPTER = 0 ``` ## Functions ### constructor 1. Deploy the TokenJar 3. Set the releaser on the token jar. 4. Update the owner on the token jar. RELEASER: 2. Deploy the Releaser. 5. Update the thresholdSetter on the releaser to the owner. 6. Update the owner on the releaser. FEE_ADAPTER: 7. Deploy the FeeAdapter. 8. Update the feeSetter to the owner. 9. Store fee tiers. 10. Update the owner on the fee adapter. UNIMinter 11. Deploy the UNIMinter - To enable the UNIMinter, the owner must call `setMinter` on the UNI contract ```solidity constructor() ; ``` --- ## ExchangeReleaser [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/releasers/ExchangeReleaser.sol) **Inherits:** [IReleaser](/home/toda/dev/phoenix-fees/forge-docs/src/src/interfaces/IReleaser.sol/interface.IReleaser.md), [ResourceManager](/home/toda/dev/phoenix-fees/forge-docs/src/src/base/ResourceManager.sol/abstract.ResourceManager.md), [Nonce](/home/toda/dev/phoenix-fees/forge-docs/src/src/base/Nonce.sol/abstract.Nonce.md) A contract that releases assets from an TokenJar in exchange for transferring a threshold amount of a resource token Inherits from ResourceManager for resource transferring functionality and Nonce for replay protection **Note:** security-contact: security@uniswap.org ## State Variables ### TOKEN_JAR ```solidity ITokenJar public immutable TOKEN_JAR ``` ## Functions ### constructor Creates a new ExchangeReleaser instance ```solidity constructor(address _resource, uint256 _threshold, address _tokenJar, address _recipient) ResourceManager(_resource, _threshold, msg.sender, _recipient); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_resource`|`address`|The address of the resource token that must be transferred| |`_threshold`|`uint256`|| |`_tokenJar`|`address`|The address of the TokenJar contract holding the assets| |`_recipient`|`address`|The address that will receive the resource tokens| ### release Releases assets to a specified recipient if the resource threshold is met ```solidity function release(uint256 _nonce, Currency[] calldata assets, address recipient) external virtual; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_nonce`|`uint256`|The nonce for the release, must equal to the contract nonce otherwise revert| |`assets`|`Currency[]`|The list of assets (addresses) to release, which may have length limits Native tokens (Ether) are represented as the zero address| |`recipient`|`address`|The address to receive the released assets, paid out by Token Jar| ### _release Internal function to handle the nonce check, transfer the RESOURCE, and call the release of assets on the TokenJar. ```solidity function _release(uint256 _nonce, Currency[] calldata assets, address recipient) internal handleNonce(_nonce); ``` --- ## Firepit [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/releasers/Firepit.sol) **Inherits:** [ExchangeReleaser](/home/toda/dev/phoenix-fees/forge-docs/src/src/releasers/ExchangeReleaser.sol/abstract.ExchangeReleaser.md) An ExchangeReleaser with recipient set to the burn address address(0xdead) and a limit on the number of currencies that can be released at any time. **Note:** security-contact: security@uniswap.org ## State Variables ### MAX_RELEASE_LENGTH Maximum number of different assets that can be released in a single call ```solidity uint256 public constant MAX_RELEASE_LENGTH = 20 ``` ## Functions ### constructor ```solidity constructor(address _resource, uint256 _threshold, address _tokenJar) ExchangeReleaser(_resource, _threshold, _tokenJar, address(0xdead)); ``` ### release Releases assets to a specified recipient if the resource threshold is met ```solidity function release(uint256 _nonce, Currency[] calldata assets, address recipient) external override; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_nonce`|`uint256`|The nonce for the release, must equal to the contract nonce otherwise revert| |`assets`|`Currency[]`|The list of assets (addresses) to release, which may have length limits Native tokens (Ether) are represented as the zero address| |`recipient`|`address`|The address to receive the released assets, paid out by Token Jar| ## Errors ### TooManyAssets Thrown when attempting to release too many assets at once ```solidity error TooManyAssets(); ``` --- ## FirepitDestination [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/crosschain/FirepitDestination.sol) **Inherits:** [Nonce](/home/toda/dev/phoenix-fees/forge-docs/src/src/base/Nonce.sol/abstract.Nonce.md), Owned a contract for receiving crosschain messages. Validates messages and releases assets from the TokenJar ## State Variables ### allowableSource the source contract that is allowed to originate messages to this contract i.e. FirepitSource updatable by owner ```solidity address public allowableSource ``` ### allowableCallers the local contract(s) that are allowed to call this contract, i.e. Message Relayers updatable by owner ```solidity mapping(address callers => bool allowed) public allowableCallers ``` ### TOKEN_JAR ```solidity TokenJar public immutable TOKEN_JAR ``` ### MINIMUM_RELEASE_GAS ```solidity uint256 public constant MINIMUM_RELEASE_GAS = 100_000 ``` ## Functions ### constructor ```solidity constructor(address _owner, address _tokenJar) Owned(_owner); ``` ### onlyAllowed ```solidity modifier onlyAllowed() ; ``` ### claimTo Calls Token Jar to release assets to a destination only callable by the messenger via the authorized L1 source contract ```solidity function claimTo(uint256 _nonce, Currency[] calldata assets, address claimer) external onlyAllowed handleNonce(_nonce); ``` ### setAllowableCallers ```solidity function setAllowableCallers(address callers, bool isAllowed) external onlyOwner; ``` ### setAllowableSource ```solidity function setAllowableSource(address source) external onlyOwner; ``` ## Events ### FailedRelease ```solidity event FailedRelease(uint256 indexed _nonce, address indexed _claimer); ``` --- ## FirepitSource [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/crosschain/FirepitSource.sol) **Inherits:** [ResourceManager](/home/toda/dev/phoenix-fees/forge-docs/src/src/base/ResourceManager.sol/abstract.ResourceManager.md), [Nonce](/home/toda/dev/phoenix-fees/forge-docs/src/src/base/Nonce.sol/abstract.Nonce.md) ## State Variables ### DEFAULT_BRIDGE_ID ```solidity uint256 public constant DEFAULT_BRIDGE_ID = 0 ``` ## Functions ### constructor TODO: Move threshold to constructor. It should not default to 0. ```solidity constructor(address _owner, address _resource) ResourceManager(_resource, 69_420, _owner, address(0xdead)); ``` ### _sendReleaseMessage ```solidity function _sendReleaseMessage( uint256 bridgeId, uint256 destinationNonce, Currency[] calldata assets, address claimer, bytes memory addtlData ) internal virtual; ``` ### release Torches the RESOURCE by sending it to the burn address and sends a cross-domain message to release the assets ```solidity function release(uint256 _nonce, Currency[] calldata assets, address claimer, uint32 l2GasLimit) external handleNonce(_nonce); ``` --- ## IFirepitDestination [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/IFirepitDestination.sol) ## Functions ### claimTo ```solidity function claimTo(uint256 _nonce, Currency[] calldata assets, address claimer) external; ``` --- ## IL1CrossDomainMessenger [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/IL1CrossDomainMessenger.sol) ## Functions ### sendMessage ```solidity function sendMessage(address _target, bytes calldata _message, uint32 _minGasLimit) external; ``` ### xDomainMessageSender ```solidity function xDomainMessageSender() external view returns (address); ``` --- ## INonce [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/base/INonce.sol) ## Functions ### nonce ```solidity function nonce() external view returns (uint256); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|The contract's nonce| ## Errors ### InvalidNonce Thrown when a user-provided nonce is not equal to the contract's nonce ```solidity error InvalidNonce(); ``` --- ## IOwned [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/base/IOwned.sol) Interface for Solmate's Owned.sol contract ## Functions ### owner ```solidity function owner() external view returns (address); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`address`|owner of the contract| ### transferOwnership Transfers ownership of the contract to a new address ```solidity function transferOwnership(address newOwner) external; ``` --- ## IReleaser [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/IReleaser.sol) **Inherits:** [IResourceManager](/home/toda/dev/phoenix-fees/forge-docs/src/src/interfaces/base/IResourceManager.sol/interface.IResourceManager.md), [INonce](/home/toda/dev/phoenix-fees/forge-docs/src/src/interfaces/base/INonce.sol/interface.INonce.md) ## Functions ### TOKEN_JAR ```solidity function TOKEN_JAR() external view returns (ITokenJar); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`ITokenJar`|Address of the Token Jar contract that will release the assets| ### release Releases assets to a specified recipient if the resource threshold is met ```solidity function release(uint256 _nonce, Currency[] calldata assets, address recipient) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_nonce`|`uint256`|The nonce for the release, must equal to the contract nonce otherwise revert| |`assets`|`Currency[]`|The list of assets (addresses) to release, which may have length limits Native tokens (Ether) are represented as the zero address| |`recipient`|`address`|The address to receive the released assets, paid out by Token Jar| --- ## IResourceManager [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/base/IResourceManager.sol) The interface for managing the resource token and its threshold value ## Functions ### RESOURCE The resource token required by parent IReleaser ```solidity function RESOURCE() external view returns (ERC20); ``` ### RESOURCE_RECIPIENT The recipient of the `RESOURCE` tokens ```solidity function RESOURCE_RECIPIENT() external view returns (address); ``` ### threshold The minimum threshold of `RESOURCE` tokens required to perform a release ```solidity function threshold() external view returns (uint256); ``` ### thresholdSetter The address authorized to set the `threshold` value ```solidity function thresholdSetter() external view returns (address); ``` ### setThresholdSetter Set the address authorized to set the `threshold` value only callable by `owner` ```solidity function setThresholdSetter(address newThresholdSetter) external; ``` ### setThreshold Set the minimum threshold of `RESOURCE` tokens required to perform a release only callable by `thresholdSetter` the `thresholdSetter` should take explicit care when updating the threshold * lowering the threshold may create instantaneous value leakage * front-running a release with an increased threshold may cause economic loss to the releaser/searcher ```solidity function setThreshold(uint256 newThreshold) external; ``` ## Errors ### Unauthorized Thrown when an unauthorized address attempts to call a restricted function ```solidity error Unauthorized(); ``` --- ## ITokenJar [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/ITokenJar.sol) The interface for releasing assets from the contract ## Functions ### releaser The releaser has exclusive access to the `release()` function ```solidity function releaser() external view returns (address); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`address`|Address of the current IReleaser| ### setReleaser Set the address of the IReleaser contract only callabe by `owner` ```solidity function setReleaser(address _releaser) external; ``` ### release Release assets to a specified recipient only callable by `releaser` ```solidity function release(Currency[] calldata assets, address recipient) external; ``` ## Errors ### Unauthorized Thrown when an unauthorized address attempts to call a restricted function ```solidity error Unauthorized(); ``` --- ## IUniswapV3FactoryOwnerActions [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/IUniswapV3FactoryOwnerActions.sol) The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees Stripped down and renamed from: https://github.com/Uniswap/v3-core/blob/d8b1c635c275d2a9450bd6a78f3fa2484fef73eb/contracts/interfaces/IUniswapV3Factory.sol ## Functions ### owner Returns the current owner of the factory Can be changed by the current owner via setOwner ```solidity function owner() external view returns (address); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`address`|The address of the factory owner| ### setOwner Updates the owner of the factory Must be called by the current owner ```solidity function setOwner(address _owner) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_owner`|`address`|The new owner of the factory| ### enableFeeAmount Enables a fee amount with the given tickSpacing Fee amounts may never be removed once enabled ```solidity function enableFeeAmount(uint24 fee, int24 tickSpacing) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`fee`|`uint24`|The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)| |`tickSpacing`|`int24`|The spacing between ticks to be enforced for all pools created with the given fee amount| ### feeAmountTickSpacing Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled A fee amount can never be removed, so this value should be hard coded or cached in the calling context ```solidity function feeAmountTickSpacing(uint24 fee) external view returns (int24); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`fee`|`uint24`|The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee| **Returns** |Name|Type|Description| |----|----|-----------| |``|`int24`|The tick spacing| --- ## IUniswapV3PoolOwnerActions [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/IUniswapV3PoolOwnerActions.sol) Contains pool methods that may only be called by the factory owner Vendored from https://github.com/Uniswap/v3-core/blob/d8b1c635c275d2a9450bd6a78f3fa2484fef73eb/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol ## Functions ### setFeeProtocol Set the denominator of the protocol's % share of the fees ```solidity function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`feeProtocol0`|`uint8`|new protocol fee for token0 of the pool| |`feeProtocol1`|`uint8`|new protocol fee for token1 of the pool| ### collectProtocol Collect the protocol fee accrued to the pool ```solidity function collectProtocol(address recipient, uint128 amount0Requested, uint128 amount1Requested) external returns (uint128 amount0, uint128 amount1); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`recipient`|`address`|The address to which collected protocol fees should be sent| |`amount0Requested`|`uint128`|The maximum amount of token0 to send, can be 0 to collect fees in only token1| |`amount1Requested`|`uint128`|The maximum amount of token1 to send, can be 0 to collect fees in only token0| **Returns** |Name|Type|Description| |----|----|-----------| |`amount0`|`uint128`|The protocol fee collected in token0| |`amount1`|`uint128`|The protocol fee collected in token1| --- ## IV3FeeAdapter [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/interfaces/IV3FeeAdapter.sol) ## Functions ### TOKEN_JAR ```solidity function TOKEN_JAR() external view returns (address); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`address`|The address where collected fees are sent.| ### FACTORY ```solidity function FACTORY() external view returns (IUniswapV3Factory); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`IUniswapV3Factory`|The Uniswap V3 Factory contract.| ### merkleRoot ```solidity function merkleRoot() external view returns (bytes32); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes32`|The current merkle root used to designate which pools have a fee enabled| ### feeSetter ```solidity function feeSetter() external view returns (address); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`address`|The authorized address to set fees-by-fee-tier AND the merkle root| ### feeTiers ```solidity function feeTiers(uint256 i) external view returns (uint24); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint24`|The fee tiers enabled on the factory| ### storeFeeTier Stores a fee tier. Must be a fee tier that exists on the Uniswap V3 Factory. ```solidity function storeFeeTier(uint24 feeTier) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`feeTier`|`uint24`|The fee tier to store.| ### defaultFees Returns the default fee value for a given fee tier. ```solidity function defaultFees(uint24 feeTier) external view returns (uint8 defaultFeeValue); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`feeTier`|`uint24`|The fee tier to query.| **Returns** |Name|Type|Description| |----|----|-----------| |`defaultFeeValue`|`uint8`|The default fee value expressed as the denominator on the inclusive interval [4, 10]. The fee value is packed (token1Fee \<\< 4 \| token0Fee)| ### enableFeeAmount Enables a new fee tier on the Uniswap V3 Factory. Only callable by `owner`. Also updates the `feeTiers` array. ```solidity function enableFeeAmount(uint24 newFeeTier, int24 tickSpacing) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`newFeeTier`|`uint24`|The fee tier to enable.| |`tickSpacing`|`int24`|The corresponding tick spacing for the new fee tier.| ### setFactoryOwner Sets the owner of the Uniswap V3 Factory. Only callable by `owner` ```solidity function setFactoryOwner(address newOwner) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`newOwner`|`address`|The new owner of the Uniswap V3 Factory.| ### collect Collects protocol fees from the specified pools to the designated `TOKEN_JAR` ```solidity function collect(CollectParams[] calldata collectParams) external returns (Collected[] memory amountsCollected); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`collectParams`|`CollectParams[]`|Array of collection parameters for each pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`amountsCollected`|`Collected[]`|Array of collected amounts for each pool.| ### setMerkleRoot Sets the merkle root used for designating which pools have the fee enabled. Only callable by `feeSetter` ```solidity function setMerkleRoot(bytes32 _merkleRoot) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_merkleRoot`|`bytes32`|The new merkle root to set.| ### setDefaultFeeByFeeTier Sets the default fee value for a specific fee tier. ```solidity function setDefaultFeeByFeeTier(uint24 feeTier, uint8 defaultFeeValue) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`feeTier`|`uint24`|The fee tier, expressed in pips, to set the default fee for.| |`defaultFeeValue`|`uint8`|The default fee value to set, expressed as the denominator on the inclusive interval [4, 10]. The fee value is packed (token1Fee \<\< 4 \| token0Fee)| ### triggerFeeUpdate Triggers a fee update for a single pool with merkle proof verification. ```solidity function triggerFeeUpdate(address pool, bytes32[] calldata merkleProof) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`pool`|`address`|The pool address to update the fee for.| |`merkleProof`|`bytes32[]`|The merkle proof corresponding to the set merkle root.| ### triggerFeeUpdate Triggers a fee update for one pair of tokens with merkle proof verification. There may be multiple pools initialized from the given pair. Assumes that token0 < token1. ```solidity function triggerFeeUpdate(address token0, address token1, bytes32[] calldata proof) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`token0`|`address`|The first token of the pair.| |`token1`|`address`|The second token of the pair.| |`proof`|`bytes32[]`|The merkle proof corresponding to the set merkle root.| ### batchTriggerFeeUpdate Triggers fee updates for multiple pairs of tokens with batch merkle proof verification. Assumes that token0 < token1 in the token pair. ```solidity function batchTriggerFeeUpdate( Pair[] calldata pairs, bytes32[] calldata proof, bool[] calldata proofFlags ) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`pairs`|`Pair[]`|The pair of two tokens. There may be multiple pools initialized from the same pair.| |`proof`|`bytes32[]`|The merkle proof corresponding to the set merkle root.| |`proofFlags`|`bool[]`|The flags for the merkle proof verification.| ### setFeeSetter Sets a new fee setter address. ```solidity function setFeeSetter(address newFeeSetter) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`newFeeSetter`|`address`|The new address authorized to set fees and merkle roots.| ## Errors ### InvalidProof Thrown when the merkle proof is invalid. ```solidity error InvalidProof(); ``` ### InvalidFeeTier Thrown when trying to set a default fee for a non-enabled fee tier. ```solidity error InvalidFeeTier(); ``` ### Unauthorized Thrown when an unauthorized address attempts to call a restricted function ```solidity error Unauthorized(); ``` ### TierAlreadyStored Thrown when trying to store a fee tier that is already stored. ```solidity error TierAlreadyStored(); ``` ## Structs ### CollectParams The input parameters for the collection. ```solidity struct CollectParams { /// @param pool The pool to collect fees from. address pool; /// @param amount0Requested The amount of token0 to collect. If this is higher than the total /// collectable amount, it will collect all but 1 wei of the total token0 allotment. uint128 amount0Requested; /// @param amount1Requested The amount of token1 to collect. If this is higher than the total /// collectable amount, it will collect all but 1 wei of the total token1 allotment. uint128 amount1Requested; } ``` ### Collected The returned amounts of token0 and token1 that are collected. ```solidity struct Collected { /// @param amount0Collected The amount of token0 that is collected. uint128 amount0Collected; /// @param amount1Collected The amount of token1 that is collected. uint128 amount1Collected; } ``` ### Pair The pair of tokens to trigger fees for. Each node in the merkle tree is the sorted pair. If one pair in the merkle tree is (USDC, USDT), all pools consisting of those tokens will be allowed to have a fee enabled. ```solidity struct Pair { /// @param token0 The first token of the pair. address token0; /// @param token1 The second token of the pair. address token1; } ``` --- ## Nonce [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/base/Nonce.sol) **Inherits:** [INonce](/home/toda/dev/phoenix-fees/forge-docs/src/src/interfaces/base/INonce.sol/interface.INonce.md) ## State Variables ### nonce ```solidity uint256 public nonce ``` ## Functions ### handleNonce ```solidity modifier handleNonce(uint256 _nonce) ; ``` --- ## OPStackFirepitSource [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/crosschain/OPStackFirepitSource.sol) **Inherits:** [FirepitSource](/home/toda/dev/phoenix-fees/forge-docs/src/src/crosschain/FirepitSource.sol/abstract.FirepitSource.md) ## State Variables ### MESSENGER ```solidity IL1CrossDomainMessenger public immutable MESSENGER ``` ### L2_TARGET ```solidity address public immutable L2_TARGET ``` ## Functions ### constructor ```solidity constructor(address _resource, address _messenger, address _l2Target) FirepitSource(msg.sender, _resource); ``` ### _sendReleaseMessage ```solidity function _sendReleaseMessage( uint256, // bridgeId uint256 destinationNonce, Currency[] calldata assets, address claimer, bytes memory addtlData ) internal override; ``` --- ## ResourceManager [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/base/ResourceManager.sol) **Inherits:** [IResourceManager](/home/toda/dev/phoenix-fees/forge-docs/src/src/interfaces/base/IResourceManager.sol/interface.IResourceManager.md), Owned A contract that holds immutable state for the resource token and the resource recipient address. It also maintains logic for managing the threshold of the resource token. ## State Variables ### threshold The minimum threshold of `RESOURCE` tokens required to perform a release ```solidity uint256 public threshold ``` ### thresholdSetter The address authorized to set the `threshold` value ```solidity address public thresholdSetter ``` ### RESOURCE The resource token required by parent IReleaser ```solidity ERC20 public immutable RESOURCE ``` ### RESOURCE_RECIPIENT The recipient of the `RESOURCE` tokens ```solidity address public immutable RESOURCE_RECIPIENT ``` ## Functions ### onlyThresholdSetter Ensures only the threshold setter can call the setThreshold function ```solidity modifier onlyThresholdSetter() ; ``` ### constructor At construction the thresholdSetter defaults to 0 and its on the owner to set. ```solidity constructor(address _resource, uint256 _threshold, address _owner, address _recipient) Owned(_owner); ``` ### setThresholdSetter Set the address authorized to set the `threshold` value only callable by `owner` ```solidity function setThresholdSetter(address _thresholdSetter) external onlyOwner; ``` ### setThreshold Set the minimum threshold of `RESOURCE` tokens required to perform a release only callable by `thresholdSetter` the `thresholdSetter` should take explicit care when updating the threshold * lowering the threshold may create instantaneous value leakage * front-running a release with an increased threshold may cause economic loss to the releaser/searcher ```solidity function setThreshold(uint256 _threshold) external onlyThresholdSetter; ``` --- ## TokenJar [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/TokenJar.sol) **Inherits:** Owned, [ITokenJar](https://github.com/Uniswap/protocol-fees/blob/main/src/interfaces/ITokenJar.sol) A singular destination for protocol fees Fees accumulate passively in this contract from external sources. Stored fees can be released by an authorized releaser contract. **Note:** security-contact: security@uniswap.org ## State Variables ### releaser The releaser has exclusive access to the `release()` function ```solidity address public releaser ``` ## Functions ### onlyReleaser Ensures only the releaser can call the release function ```solidity modifier onlyReleaser() ; ``` ### constructor creates an token jar where the deployer is the initial owner during deployment, the deployer SHOULD set the releaser address and transfer ownership ```solidity constructor() Owned(msg.sender); ``` ### release Release assets to a specified recipient only callable by `releaser` ```solidity function release(Currency[] calldata assets, address recipient) external onlyReleaser; ``` ### setReleaser Set the address of the IReleaser contract only callabe by `owner` ```solidity function setReleaser(address _releaser) external onlyOwner; ``` ### receive ```solidity receive() external payable; ``` --- ## UnauthorizedCall [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/crosschain/FirepitDestination.sol) ```solidity error UnauthorizedCall(); ``` --- ## V3FeeAdapter [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/feeAdapters/V3FeeAdapter.sol) **Inherits:** [IV3FeeAdapter](/home/toda/dev/phoenix-fees/forge-docs/src/src/interfaces/IV3FeeAdapter.sol/interface.IV3FeeAdapter.md), Owned A contract that allows the setting and collecting of protocol fees per pool, and adding new fee tiers to the Uniswap V3 Factory. This contract is ownable. The owner can set the merkle root for proving protocol fee amounts per pool, set new fee tiers on Uniswap V3, and change the owner of this contract. Note that this contract will be the set owner on the Uniswap V3 Factory. **Note:** security-contact: security@uniswap.org ## State Variables ### FACTORY ```solidity IUniswapV3Factory public immutable FACTORY ``` ### TOKEN_JAR ```solidity address public immutable TOKEN_JAR ``` ### merkleRoot ```solidity bytes32 public merkleRoot ``` ### feeSetter ```solidity address public feeSetter ``` ### defaultFees Returns the default fee value for a given fee tier. ```solidity mapping(uint24 feeTier => uint8 defaultFeeValue) public defaultFees ``` ### feeTiers Returns four enabled fee tiers: 100, 500, 3000, 10000. May return more if more are enabled. ```solidity uint24[] public feeTiers ``` ## Functions ### onlyFeeSetter Ensures only the fee setter can call the setMerkleRoot and setDefaultFeeByFeeTier functions ```solidity modifier onlyFeeSetter() ; ``` ### constructor At construction, the fee setter defaults to 0 and its on the owner to set. ```solidity constructor(address _factory, address _tokenJar) Owned(msg.sender); ``` ### storeFeeTier Stores a fee tier. Must be a fee tier that exists on the Uniswap V3 Factory. ```solidity function storeFeeTier(uint24 feeTier) public; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`feeTier`|`uint24`|The fee tier to store.| ### enableFeeAmount Enables a new fee tier on the Uniswap V3 Factory. Only callable by `owner`. Also updates the `feeTiers` array. ```solidity function enableFeeAmount(uint24 fee, int24 tickSpacing) external onlyOwner; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`fee`|`uint24`|| |`tickSpacing`|`int24`|The corresponding tick spacing for the new fee tier.| ### setFactoryOwner ```solidity function setFactoryOwner(address newOwner) external onlyOwner; ``` ### collect Collects protocol fees from the specified pools to the designated `TOKEN_JAR` ```solidity function collect(CollectParams[] calldata collectParams) external returns (Collected[] memory amountsCollected); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`collectParams`|`CollectParams[]`|Array of collection parameters for each pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`amountsCollected`|`Collected[]`|Array of collected amounts for each pool.| ### setMerkleRoot Sets the merkle root used for designating which pools have the fee enabled. Only callable by `feeSetter` ```solidity function setMerkleRoot(bytes32 _merkleRoot) external onlyFeeSetter; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_merkleRoot`|`bytes32`|The new merkle root to set.| ### setDefaultFeeByFeeTier Sets the default fee value for a specific fee tier. ```solidity function setDefaultFeeByFeeTier(uint24 feeTier, uint8 defaultFeeValue) external onlyFeeSetter; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`feeTier`|`uint24`|The fee tier, expressed in pips, to set the default fee for.| |`defaultFeeValue`|`uint8`|The default fee value to set, expressed as the denominator on the inclusive interval [4, 10]. The fee value is packed (token1Fee \<\< 4 \| token0Fee)| ### setFeeSetter Sets a new fee setter address. ```solidity function setFeeSetter(address newFeeSetter) external onlyOwner; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`newFeeSetter`|`address`|The new address authorized to set fees and merkle roots.| ### triggerFeeUpdate Triggers a fee update for a single pool with merkle proof verification. ```solidity function triggerFeeUpdate(address pool, bytes32[] calldata proof) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`pool`|`address`|The pool address to update the fee for.| |`proof`|`bytes32[]`|| ### triggerFeeUpdate Triggers a fee update for a single pool with merkle proof verification. ```solidity function triggerFeeUpdate(address token0, address token1, bytes32[] calldata proof) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`token0`|`address`|| |`token1`|`address`|| |`proof`|`bytes32[]`|| ### batchTriggerFeeUpdate Triggers fee updates for multiple pairs of tokens with batch merkle proof verification. Assumes that token0 < token1 in the token pair. ```solidity function batchTriggerFeeUpdate( Pair[] calldata pairs, bytes32[] calldata proof, bool[] calldata proofFlags ) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`pairs`|`Pair[]`|The pair of two tokens. There may be multiple pools initialized from the same pair.| |`proof`|`bytes32[]`|The merkle proof corresponding to the set merkle root.| |`proofFlags`|`bool[]`|The flags for the merkle proof verification.| ### _setProtocolFeesForPair ```solidity function _setProtocolFeesForPair(address token0, address token1) internal; ``` ### _setProtocolFee ```solidity function _setProtocolFee(address pool, uint24 feeTier) internal; ``` ### _doubleHash ```solidity function _doubleHash(address token0, address token1) internal pure returns (bytes32 poolHash); ``` ### _feeTierExists ```solidity function _feeTierExists(uint24 feeTier) internal view returns (bool); ``` --- ## V4FeeAdapter [Git Source](https://github.com/Uniswap/phoenix-fees/blob/f7ccbcc4f1be2c8485a362f78f4f1ea34145b2b0/src/feeAdapters/V4FeeAdapter.sol) **Inherits:** Owned Triggers the collection of protocol fees to a predefined token jar. ## State Variables ### POOL_MANAGER ```solidity IPoolManager public immutable POOL_MANAGER ``` ### tokenJar ```solidity address public tokenJar ``` ### merkleRoot ```solidity bytes32 public merkleRoot ``` ## Functions ### constructor ```solidity constructor(address _poolManager, address _tokenJar, address _owner) Owned(_owner); ``` ### collect Collects the protocol fees for the given currencies to the token jar. ```solidity function collect( Currency[] calldata currency, uint256[] calldata amountRequested, uint256[] calldata amountExpected ) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`Currency[]`|The currencies to collect fees for.| |`amountRequested`|`uint256[]`|The amount of each currency to request.| |`amountExpected`|`uint256[]`|The amount of each currency that is expected to be collected.| ### setMerkleRoot Sets the merkle root for the fee adapter. only callable by owner ```solidity function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_merkleRoot`|`bytes32`|The merkle root to set.| ### triggerFeeUpdate Triggers the fee update for the given pool key. ```solidity function triggerFeeUpdate( PoolKey calldata _poolKey, uint24 newProtocolFee, bytes32[] calldata proof ) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_poolKey`|`PoolKey`|The pool key to update the fee for.| |`newProtocolFee`|`uint24`|The new protocol fee to set.| |`proof`|`bytes32[]`|The merkle proof corresponding to the set merkle root. Merkle root is generated from leaves of keccak256(abi.encode(poolKey, protocolFee)).| ## Errors ### AmountCollectedTooLow Thrown when the amount collected is less than the amount expected. ```solidity error AmountCollectedTooLow(uint256 amountCollected, uint256 amountExpected); ``` ### InvalidProof Thrown when the merkle proof is invalid. ```solidity error InvalidProof(); ``` --- ## Overview(The-compact) [The Compact 🤝](https://github.com/Uniswap/the-compact) is an ownerless ERC6909 contract that facilitates the voluntary formation and mediation of reusable **resource locks**. It enables tokens to be credibly committed to be spent in exchange for performing actions across arbitrary, asynchronous environments, and claimed once the specified conditions have been met. ## Key Components ### Resource Locks Resource locks are created when depositors place tokens (ERC20 or native) into The Compact. Each lock is represented by an ERC6909 token with four key properties: - The underlying token - The allocator (prevents double-spending) - The scope (single-chain or multichain) - The reset period (for forced withdrawals) ### Compacts A compact is a commitment created by a resource lock owner (sponsor) that allows tokens to be claimed under specified conditions. Compacts use EIP-712 typed structured data for signatures and can be: - **Single**: One resource lock on one chain - **Batch**: Multiple resource locks on one chain - **Multichain**: Resource locks across multiple chains ### Key Actors - **Sponsors**: Deposit tokens and create compacts - **Allocators**: Prevent double-spending and validate transfers - **Arbiters**: Verify conditions and process claims - **Claimants**: Fulfill conditions and receive tokens - **Emissaries**: Provide fallback signature verification ## How It Works 1. **Deposit**: Sponsors deposit tokens to create resource locks (ERC6909 tokens) 2. **Create Compact**: Sponsors sign or register compacts specifying conditions 3. **Fulfill**: Claimants meet the specified conditions 4. **Claim**: Arbiters verify and process claims to transfer tokens ## Trust Model The Compact operates on a trust-minimized model where: - Sponsors trust allocators won't censor valid requests (but can force withdraw if needed). - Sponsors trust arbiters will only process valid claims. - Claimants trust allocators will maintain lock integrity. - Claimants trust arbiters will process valid claims. The Compact V1 has undergone two independent security reviews by [OpenZeppelin](https://openzeppelin.com) and [Spearbit Cantina](https://cantina.xyz). ## Deployments The Compact is deployed at the same address across multiple chains: | Network | Address | |---------|---------| | Ethereum Mainnet | `0x00000000000000171ede64904551eeDF3C6C9788` | | Base | `0x00000000000000171ede64904551eeDF3C6C9788` | | Unichain | `0x00000000000000171ede64904551eeDF3C6C9788` | > The Compact uses a deterministic deployment address, ensuring the same address across all supported networks. The Compact uses a permissionless deployment process. Anyone can deploy the protocol to a new EVM chain by submitting a transaction with the same `to` and `data` arguments. This ensures the same deterministic address across all chains and enables the creation of compacts that commit resource locks across multiple chains. --- ## Allocators Allocators are crucial infrastructure components in The Compact protocol that ensure resource lock integrity and prevent double-spending. ## Role and Responsibilities Each resource lock is mediated by an allocator with four primary responsibilities: 1. **Preventing Double-Spending**: Ensuring sponsors don't commit the same tokens to multiple compacts or transfer away committed funds 2. **Validating Transfers**: Attesting to standard ERC6909 transfers of resource lock tokens 3. **Authorizing Claims**: Validating claims against resource locks 4. **Nonce Management**: Ensuring nonces are not reused for claims ## Registration Allocators must be registered with The Compact before they can be assigned to locks. ### Registration Requirements Anyone can register an allocator if one of three conditions is met: - The caller is the allocator address being registered - The allocator address contains code - A proof is supplied representing valid create2 deployment parameters ### Registration Function ```solidity function __registerAllocator( address allocator, bytes calldata proof ) external returns (uint96 allocatorId) ``` ### Create2 Proof Format When registering an allocator that doesn't yet exist but will be deployed via create2, provide an 85-byte proof: ``` 0xff ++ factory ++ salt ++ initcode hash ``` This allows pre-registration of deterministic addresses. ## IAllocator Interface All allocators must implement the `IAllocator` interface: ### attest Function Called on standard ERC6909 transfers to validate them: ```solidity function attest( address operator, address from, address to, uint256 id, uint256 amount ) external returns (bytes4) ``` **Requirements**: - Must verify the transfer is safe - Must return `IAllocator.attest.selector` on success ### authorizeClaim Function Called by The Compact during claim processing for on-chain authorization: ```solidity function authorizeClaim( bytes32 claimHash, address arbiter, address sponsor, uint256 nonce, uint256 expires, uint256[2][] calldata idsAndAmounts, bytes calldata allocatorData ) external returns (bytes4) ``` **Parameters**: - `claimHash`: The hash of the claim being processed - `arbiter`: The arbiter processing the claim - `sponsor`: The sponsor of the compact - `nonce`: The nonce for replay protection - `expires`: Expiration timestamp - `idsAndAmounts`: Array of [id, amount] pairs - `allocatorData`: Custom data (e.g., signatures) **Requirements**: - Must return `IAllocator.authorizeClaim.selector` on success ### isClaimAuthorized Function Off-chain view function to check if a claim would be authorized: ```solidity function isClaimAuthorized( bytes32 claimHash, address arbiter, address sponsor, uint256 nonce, uint256 expires, uint256[2][] calldata idsAndAmounts, bytes calldata allocatorData ) external view returns (bool) ``` Should perform the same authorization checks as `authorizeClaim` but as a view function. ## Allocator Data The `allocatorData` parameter allows allocators to implement custom authorization logic: - Can contain signatures from off-chain systems - May include merkle proofs or other authorization evidence - The format is entirely defined by each allocator implementation ## Nonce Management Allocators can directly consume nonces to invalidate compacts: ```solidity function consume(uint256 nonce) external ``` This emits a `NonceConsumedDirectly` event and prevents any compact using that nonce from being claimed. Check if a nonce has been consumed: ```solidity function hasConsumedAllocatorNonce( address allocator, uint256 nonce ) external view returns (bool) ``` ## Implementation Patterns ### On-chain Allocators Purely on-chain allocators can: - Track balances in contract storage - Implement authorization logic directly - Use on-chain randomness or oracles ### Hybrid Allocators Combine on-chain and off-chain components: - Off-chain tracking and signature generation - On-chain signature verification - Balance attestation via `allocatorData` ### Sample Implementations Two basic sample implementations are available: - **[Smallocator](https://github.com/uniswap/smallocator)**: A minimal implementation - **[Autocator](https://github.com/uniswap/autocator)**: An automated allocator ## Trust Assumptions ### For Sponsors - Allocators won't unduly censor valid requests against fully funded locks - Sponsors can initiate forced withdrawals if allocators become unresponsive ### For Claimants - Allocators are sound and won't allow resource locks to become underfunded - Allocators will properly track and enforce balance constraints ## Forced Withdrawal Mechanism To protect sponsors from unresponsive or malicious allocators, The Compact implements a forced withdrawal mechanism that allows sponsors to bypass allocator authorization after a waiting period. ### How It Works 1. **Initiation**: A sponsor calls `enableForcedWithdrawal(uint256 id)` to signal their intent to withdraw without allocator approval. 2. **Timelock Period**: The protocol enforces a waiting period equal to the resource lock's `resetPeriod`. 3. **Execution**: After the timelock expires, the sponsor can call `forcedWithdrawal(uint256 id, address recipient, uint256 amount)` to withdraw the underlying tokens. A sponsor can call `disableForcedWithdrawal(uint256 id)` at any time before execution to cancel the process. Once their withdrawal status has been set as enabled, that state will persist until it is explicitly disabled; should they wish to utilize the underlying resource lock again, the withdrawal status will first need to be disabled. ### Functions ```solidity // Enable forced withdrawal for a resource lock function enableForcedWithdrawal(uint256 id) external // Disable a pending forced withdrawal function disableForcedWithdrawal(uint256 id) external // Execute forced withdrawal after timelock expires function forcedWithdrawal( uint256 id, address recipient, uint256 amount ) external ``` ### Security Considerations - The timelock period provides adequate notice to all parties about the withdrawal intent - This prevents sudden balance changes that could lead to equivocation - The reset period is chosen by the sponsor when creating the resource lock - Shorter reset periods provide faster exit but may offer less security to claimants ### Checking Withdrawal Status ```solidity function getForcedWithdrawalStatus( address account, uint256 id ) external view returns (ForcedWithdrawalStatus status, uint256 withdrawableAt) ``` Status can be: - `Disabled`: No forced withdrawal is pending - `Pending`: Withdrawal initiated but timelock not expired - `Enabled`: Timelock expired and withdrawal can be executed ## Events ```solidity event AllocatorRegistered( uint96 allocatorId, address allocator ) event NonceConsumedDirectly( address indexed allocator, uint256 nonce ) ``` ## Error Handling Common allocator-related errors: - `InvalidAllocation(address allocator)`: Invalid allocator used - `InvalidBatchAllocation()`: Batch allocation is invalid - `InvalidRegistrationProof(address allocator)`: Registration proof is invalid - `InconsistentAllocators()`: Allocators are inconsistent across batch operations - `InvalidNonce(address account, uint256 nonce)`: Nonce is invalid or already consumed --- ## Arbiters Arbiters are entities responsible for verifying and submitting claims against resource locks in The Compact protocol. When a sponsor creates a compact, they assign an arbiter per chain. That arbiter is the sole account that can trigger a claim against the compact on the respective chain and determines which accounts receive the locked balances and in what amounts. ## Role and Responsibilities Arbiters serve as intermediaries between sponsors and claimants with these key responsibilities: 1. **Claim Verification**: Validate that claim conditions are met before submission 2. **Claim Submission**: Submit claims to The Compact on behalf of claimants 3. **Recipient Allocation**: Determine which accounts receive funds and their amounts 4. **Witness Data Validation**: Verify any witness data included in compacts 5. **Cross-chain Coordination**: For multichain compacts, coordinate claims across different chains ## Arbiter Selection Arbiters are specified per compact or per element (in multichain compacts): ### Single and Batch Compacts ```solidity struct Compact { address arbiter; // The designated arbiter for this compact address sponsor; uint256 nonce; uint256 expires; // ... } ``` ### Multichain Compacts ```solidity struct Element { address arbiter; // Can be different per chain uint256 chainId; Lock[] commitments; Mandate mandate; // Required for multichain } ``` ## Specifying Claimants When submitting a claim, arbiters specify claimants through a `Component` struct that encodes both the recipient and the destination format: ```solidity struct Component { uint256 claimant; // Encodes both lockTag and recipient address uint256 amount; // The amount of tokens to claim } ``` ### Claimant Encoding The `claimant` field packs two pieces of information: - **Upper 96 bits**: `bytes12 lockTag` (destination format) - **Lower 160 bits**: `address recipient` (who receives the tokens) ### Destination Options Based on the `lockTag` in the claimant field, arbiters can direct claimed resources to three different destinations: #### 1. Direct Transfer (Keep as ERC6909) When the `lockTag` matches the original resource lock's tag, the ERC6909 tokens are transferred directly to the recipient. This maintains the same lock properties (allocator, reset period, scope). #### 2. Convert to New Resource Lock When the `lockTag` is non-zero but different from the original, the tokens are converted to a new resource lock with different properties. This allows changing allocators, reset periods, or scopes while keeping funds locked. #### 3. Withdraw to Underlying Token When the `lockTag` is `bytes12(0)`, the ERC6909 tokens are burned and the underlying tokens (native or ERC20) are withdrawn to the recipient. This fully exits The Compact system. :::important To prevent griefing attacks (e.g., via malicious receive hooks or intentionally underpaying gas), The Compact implements a withdrawal fallback mechanism: 1. The protocol first attempts withdrawals with half the available gas 2. If this fails (and sufficient gas remains above a benchmarked stipend), it falls back to a direct ERC6909 transfer to the recipient This mechanism ensures that claims cannot be maliciously blocked through receive hook manipulation, while still preserving the intended withdrawal functionality under normal conditions. The required gas stipends for this fallback are determined through benchmarking, which measures: - Cold account access costs - Typical ERC20 transfer gas requirements - Native token transfer gas requirements The benchmark can be re-run at any time through a call to `__benchmark`. ::: ### Example ```solidity // Example: Arbiter specifying three different destinations Component[] memory claimants = new Component[](3); // Direct transfer - keep same lock properties claimants[0] = Component({ claimant: uint256(uint160(alice)) | (uint256(originalLockTag) << 160), amount: 100e18 }); // Convert to new lock with different allocator claimants[1] = Component({ claimant: uint256(uint160(bob)) | (uint256(newLockTag) << 160), amount: 50e18 }); // Withdraw to underlying token claimants[2] = Component({ claimant: uint256(uint160(charlie)), // lockTag is 0 amount: 25e18 }); ``` ## Submitting Claims Arbiters submit claims using one of six available claim functions based on: - **Single vs Batch**: Whether claiming against one or multiple resource locks on a chain - **Single-chain vs Multichain**: Whether the compact spans one or multiple chains - **Notarized vs Exogenous** (multichain only): Whether claiming on the primary signed chain or other chains ### Single-Chain Claims #### claim (Compact) For single resource lock on a single chain: ```solidity function claim(Claim calldata claimPayload) external returns (bytes32 claimHash) ``` #### batchClaim (BatchCompact) For multiple resource locks on a single chain: ```solidity function batchClaim(BatchClaim calldata claimPayload) external returns (bytes32 claimHash) ``` ### Multichain Claims #### multichainClaim (MultichainCompact - Notarized) For single resource lock on the notarized chain (domain matches signature): ```solidity function multichainClaim( MultichainClaim calldata claimPayload ) external returns (bytes32 claimHash) ``` #### exogenousClaim (MultichainCompact - Exogenous) For single resource lock on exogenous chains (not the notarized chain): ```solidity function exogenousClaim( ExogenousMultichainClaim calldata claimPayload ) external returns (bytes32 claimHash) ``` #### batchMultichainClaim (BatchMultichainClaim - Notarized) For multiple resource locks on the notarized chain: ```solidity function batchMultichainClaim( BatchMultichainClaim calldata claimPayload ) external returns (bytes32 claimHash) ``` #### exogenousBatchClaim (BatchMultichainClaim - Exogenous) For multiple resource locks on exogenous chains: ```solidity function exogenousBatchClaim( ExogenousBatchMultichainClaim calldata claimPayload ) external returns (bytes32 claimHash) ``` ## Trust Model ### For Sponsors - Sponsors trust arbiters to: - Only submit valid claims that meet agreed-upon conditions - Not collude with claimants to drain resource locks prematurely - Properly verify witness data before claim submission ### For Claimants - Claimants trust arbiters to: - Submit claims promptly when conditions are met - Not censor valid claims - Distribute claimed resources according to compact terms ## Arbiter Authorization When an arbiter submits a claim, The Compact verifies authorization in this order: 1. **Arbiter is Caller**: The `msg.sender` must match the arbiter specified in the compact 2. **Claim Validity**: The claim must be valid (not expired, correct nonce, etc.) 3. **Sponsor Signature**: The sponsor's signature must authorize the compact (unless registered) 4. **Allocator Authorization**: The allocator must approve the claim ## Best Practices ### For Arbiter Selection - Choose arbiters with proven track records - Consider using decentralized arbiter networks for reduced trust requirements - Implement arbiter reputation systems for accountability ### For Arbiter Implementation - Validate all claim parameters before submission - Implement robust witness data verification - Maintain audit logs of all claim submissions - Use secure key management for arbiter accounts ## Common Patterns ### Automated Arbiters Smart contracts can act as arbiters to provide trustless claim verification: - Oracle-based arbiters that verify external conditions - Time-locked arbiters for scheduled releases - Multi-signature arbiters requiring multiple approvals ### Arbiter Networks Multiple arbiters can be coordinated through: - Consensus mechanisms for claim approval - Reputation-based selection systems - Stake-based security models ## Error Handling Common arbiter-related errors: - `InvalidArbiter()`: The caller is not the designated arbiter - `ClaimExpired()`: The claim has passed its expiration time - `InvalidClaimSignature()`: The sponsor's signature is invalid - `UnauthorizedClaim()`: The arbiter is not authorized for this claim ## Events Key events related to arbiter actions: ```solidity event Claim( address indexed sponsor, address indexed allocator, address indexed arbiter, bytes32 claimHash, uint256 nonce ) ``` ## Security Considerations ### Arbiter Compromise - If an arbiter's key is compromised, they could submit unauthorized claims - Sponsors should monitor for suspicious claim activity - Consider using time delays or multi-signature requirements for high-value compacts ### Arbiter Censorship - Arbiters could refuse to submit valid claims - Sponsors can mitigate by: - Using multiple arbiters - Implementing arbiter replacement mechanisms - Setting appropriate expiration times ### Front-running Protection - Arbiters should use private mempools or commit-reveal schemes when appropriate - Consider using flashbot bundles for sensitive claims --- ## Compacts & EIP-712 A **compact** is an agreement created by a sponsor that allows their locked resources to be claimed under specified conditions. The Compact protocol uses EIP-712 typed structured data for creating and verifying signatures for these agreements. ## Compact Types ### 1. Single Compact For single resource lock operations on a single chain. ```solidity struct Compact { address arbiter; // The account tasked with verifying and submitting the claim address sponsor; // The account to source the tokens from uint256 nonce; // A parameter to enforce replay protection, scoped to allocator uint256 expires; // The time at which the claim expires bytes12 lockTag; // A tag representing the allocator, reset period, and scope address token; // The locked token, or address(0) for native tokens uint256 amount; // The amount of ERC6909 tokens to commit from the lock // (Optional) Witness data may follow: // Mandate mandate; } ``` ### 2. Batch Compact For allocating multiple resource locks on a single chain. ```solidity struct BatchCompact { address arbiter; // The account tasked with verifying and submitting the claim address sponsor; // The account to source the tokens from uint256 nonce; // A parameter to enforce replay protection, scoped to allocator uint256 expires; // The time at which the claim expires Lock[] commitments; // The committed locks with lock tags, tokens, & amounts // (Optional) Witness data may follow: // Mandate mandate; } struct Lock { bytes12 lockTag; // A tag representing the allocator, reset period, and scope address token; // The locked token, or address(0) for native tokens uint256 amount; // The maximum committed amount of tokens } ``` ### 3. Multichain Compact For allocating one or more resource locks across multiple chains. ```solidity struct MultichainCompact { address sponsor; // The account to source the tokens from uint256 nonce; // A parameter to enforce replay protection, scoped to allocator uint256 expires; // The time at which the claim expires Element[] elements; // Arbiter, chainId, commitments, and mandate for each chain } struct Element { address arbiter; // The account tasked with verifying and submitting the claim uint256 chainId; // The chainId where the tokens are located Lock[] commitments; // The committed locks with lock tags, tokens, & amounts // Witness data MUST follow (mandatory for multichain compacts): Mandate mandate; } ``` ## Witness Structure The witness mechanism (`Mandate` struct) allows extending compacts with additional data for specifying conditions or parameters for a claim. ### Format The witness is always a `Mandate` struct appended to the compact: ```solidity Compact(..., Mandate mandate)Mandate(uint256 myArg, bytes32 otherArg) ``` ### Witness Typestring The `witnessTypestring` provided during a claim should be the arguments *inside* the `Mandate` struct (e.g., `uint256 myArg,bytes32 otherArg`), followed by any nested structs. ### Nested Structs EIP-712 requires nested structs to be ordered alphanumerically after the top-level struct in the typestring. Prefix nested structs with "Mandate" (e.g., `MandateCondition`) to ensure correct ordering. Example witness typestring: ``` MandateCondition condition,uint256 arg)MandateCondition(bool flag,uint256 val ``` > ⚠️ **Important**: Do not include the closing parenthesis in your witness typestring. It will be added by the protocol during dynamic typestring construction. ## Permit2 Integration The Compact supports integration with Permit2 for gasless deposits: ### CompactDeposit For basic Permit2 deposits: ```solidity CompactDeposit(bytes12 lockTag,address recipient) ``` ### Activation Combines deposits with single compact registration: ```solidity Activation(address activator,uint256 id,Compact compact)Compact(...)Mandate(...) ``` ### BatchActivation Combines deposits with batch compact registration: ```solidity BatchActivation(address activator,uint256[] ids,Compact compact)Compact(...)Mandate(...) ``` ## CompactCategory Enum Used to distinguish between different types of compacts when using Permit2 integration: ```solidity enum CompactCategory { Compact, // 0 BatchCompact, // 1 MultichainCompact // 2 } ``` ## Registration As an alternative to signing EIP-712 payloads, compacts can be registered directly on The Compact contract: ### Register Functions ```solidity // Register a single compact function register(bytes32 claimHash, bytes32 typehash) external; // Register multiple compacts function registerMultiple(bytes32[] calldata claimHashes, bytes32[] calldata typehashes) external; // Register on behalf of sponsor function registerFor( address sponsor, bytes32 claimHash, bytes32 typehash, bytes memory sponsorSignature ) external; ``` ### Registration Event When a compact is registered, the following event is emitted: ```solidity event CompactRegistered( address indexed sponsor, bytes32 claimHash, bytes32 typehash ); ``` This event is emitted when a compact is registered via `register`, `registerMultiple`, or any of the combined deposit-and-register functions. ### Check Registration Status ```solidity function isRegistered( address sponsor, bytes32 claimHash, bytes32 typehash ) external view returns (bool); ``` ## Signature Verification When a claim is submitted for a non-registered compact, The Compact verifies the sponsor's authorization in the following order: 1. **Caller is Sponsor**: If `msg.sender == sponsor`, authorization is granted 2. **ECDSA Signature**: Attempt standard ECDSA signature verification 3. **EIP-1271 `isValidSignature`**: If ECDSA fails, call `isValidSignature` on the sponsor's address 4. **Emissary `verifyClaim`**: If EIP-1271 fails, call the emissary's `verifyClaim` function --- ## Core Interfaces The Compact protocol is composed of several key interfaces that define its functionality. ## ITheCompact The core interface for The Compact protocol, providing functions for deposits, transfers, registration, and management. ### Deposit Functions #### Basic Deposits ```solidity // Deposit native tokens function depositNative( bytes12 lockTag, address recipient ) external payable returns (uint256 id); // Deposit ERC20 tokens function depositERC20( address token, bytes12 lockTag, uint256 amount, address recipient ) external returns (uint256 id); // Batch deposits (native + ERC20) function batchDeposit( uint256[2][] calldata idsAndAmounts, address recipient ) external payable returns (bool); ``` #### Permit2 Deposits ```solidity // Single deposit via Permit2 function depositERC20ViaPermit2( ISignatureTransfer.PermitTransferFrom calldata permit, address depositor, bytes12 lockTag, address recipient, bytes calldata signature ) external returns (uint256 id); // Batch deposit via Permit2 function batchDepositViaPermit2( address depositor, ISignatureTransfer.TokenPermissions[] calldata permitted, DepositDetails calldata details, address recipient, bytes calldata signature ) external payable returns (uint256[] memory ids); ``` #### Deposit + Register Combinations ```solidity // Native deposit and register function depositNativeAndRegister( bytes12 lockTag, bytes32 claimHash, bytes32 typehash ) external payable returns (uint256 id); // Native deposit and register for another function depositNativeAndRegisterFor( address recipient, bytes12 lockTag, address arbiter, uint256 nonce, uint256 expires, bytes32 typehash, bytes32 witness ) external payable returns (uint256 id, bytes32 claimHash); // ERC20 deposit and register function depositERC20AndRegister( address token, bytes12 lockTag, uint256 amount, bytes32 claimHash, bytes32 typehash ) external returns (uint256 id); // ERC20 deposit and register for another function depositERC20AndRegisterFor( address recipient, address token, bytes12 lockTag, uint256 amount, address arbiter, uint256 nonce, uint256 expires, bytes32 typehash, bytes32 witness ) external returns (uint256 id, bytes32 claimHash, uint256 registeredAmount); // Batch deposit and register multiple function batchDepositAndRegisterMultiple( uint256[2][] calldata idsAndAmounts, bytes32[2][] calldata claimHashesAndTypehashes ) external payable returns (bool); // Batch deposit and register for another function batchDepositAndRegisterFor( address recipient, uint256[2][] calldata idsAndAmounts, address arbiter, uint256 nonce, uint256 expires, bytes32 typehash, bytes32 witness ) external payable returns (bytes32 claimHash, uint256[] memory registeredAmounts); // Deposit via Permit2 and register function depositERC20AndRegisterViaPermit2( ISignatureTransfer.PermitTransferFrom calldata permit, address depositor, bytes12 lockTag, bytes32 claimHash, CompactCategory compactCategory, string calldata witness, bytes calldata signature ) external returns (uint256 id); // Batch deposit via Permit2 and register function batchDepositAndRegisterViaPermit2( address depositor, ISignatureTransfer.TokenPermissions[] calldata permitted, DepositDetails calldata details, bytes32 claimHash, CompactCategory compactCategory, string calldata witness, bytes calldata signature ) external payable returns (uint256[] memory ids); ``` ### Allocated Transfers ```solidity // Transfer single ID to multiple recipients with allocator approval function allocatedTransfer( AllocatedTransfer calldata transfer ) external returns (bool); // Transfer multiple IDs function allocatedBatchTransfer( AllocatedBatchTransfer calldata transfer ) external returns (bool); ``` ### Registration Functions ```solidity // Register single compact function register( bytes32 claimHash, bytes32 typehash ) external returns (bool); // Register multiple compacts function registerMultiple( bytes32[2][] calldata claimHashesAndTypehashes ) external returns (bool); // Register compact on behalf of sponsor function registerFor( bytes32 typehash, address arbiter, address sponsor, uint256 nonce, uint256 expires, bytes12 lockTag, address token, uint256 amount, bytes32 witness, bytes calldata sponsorSignature ) external returns (bytes32 claimHash); // Register batch compact on behalf of sponsor function registerBatchFor( bytes32 typehash, address arbiter, address sponsor, uint256 nonce, uint256 expires, bytes32 idsAndAmountsHash, bytes32 witness, bytes calldata sponsorSignature ) external returns (bytes32 claimHash); // Register multichain compact on behalf of sponsor function registerMultichainFor( bytes32 typehash, address sponsor, uint256 nonce, uint256 expires, bytes32 elementsHash, uint256 notarizedChainId, bytes calldata sponsorSignature ) external returns (bytes32 claimHash); ``` ### Forced Withdrawals ```solidity // Enable forced withdrawal function enableForcedWithdrawal( uint256 id ) external returns (uint256 withdrawableAt); // Disable forced withdrawal function disableForcedWithdrawal( uint256 id ) external returns (bool); // Execute forced withdrawal function forcedWithdrawal( uint256 id, address recipient, uint256 amount ) external returns (bool); ``` ### Management Functions ```solidity // Emissary management function assignEmissary( bytes12 lockTag, address emissary ) external returns (bool); function scheduleEmissaryAssignment( bytes12 lockTag ) external returns (uint256 emissaryAssignmentAvailableAt); // Allocator management function __registerAllocator( address allocator, bytes calldata proof ) external returns (uint96 allocatorId); // Consume nonces (for allocators) function consume( uint256[] calldata nonces ) external returns (bool); // Benchmark withdrawal costs function __benchmark(bytes32 salt) external payable; ``` ### View Functions ```solidity function getLockDetails(uint256 id) external view returns ( address token, address allocator, ResetPeriod resetPeriod, Scope scope, bytes12 lockTag ); function isRegistered( address sponsor, bytes32 claimHash, bytes32 typehash ) external view returns (bool isActive); function getForcedWithdrawalStatus( address account, uint256 id ) external view returns ( ForcedWithdrawalStatus status, uint256 forcedWithdrawalAvailableAt ); function getEmissaryStatus( address sponsor, bytes12 lockTag ) external view returns ( EmissaryStatus status, uint256 emissaryAssignmentAvailableAt, address currentEmissary ); function hasConsumedAllocatorNonce( uint256 nonce, address allocator ) external view returns (bool consumed); function getRequiredWithdrawalFallbackStipends() external view returns ( uint256 nativeTokenStipend, uint256 erc20TokenStipend ); function DOMAIN_SEPARATOR() external view returns (bytes32); function name() external pure returns (string memory); ``` ## ITheCompactClaims The claims interface provides endpoints for arbiters to settle compacts. ### Single Chain Claims ```solidity // Standard single-chain, single-ID claim function claim(Claim calldata claimPayload) external returns (bytes32 claimHash); // Multiple IDs on a single chain function batchClaim(BatchClaim calldata claimPayload) external returns (bytes32 claimHash); ``` ### Multichain Claims ```solidity // For the notarized chain of a multichain compact function multichainClaim( MultichainClaim calldata claimPayload ) external returns (bytes32 claimHash); // For an exogenous chain of a multichain compact function exogenousClaim( ExogenousMultichainClaim calldata claimPayload ) external returns (bytes32 claimHash); // Batch versions for multiple resource locks function batchMultichainClaim( BatchMultichainClaim calldata claimPayload ) external returns (bytes32 claimHash); function exogenousBatchClaim( ExogenousBatchMultichainClaim calldata claimPayload ) external returns (bytes32 claimHash); ``` ### Claim Struct ```solidity struct Claim { bytes allocatorData; bytes sponsorSignature; address sponsor; uint256 nonce; uint256 expires; bytes32 witness; string witnessTypestring; uint256 id; uint256 allocatedAmount; Component[] claimants; } ``` ### Component Struct ```solidity struct Component { uint256 claimant; // The lockTag + recipient uint256 amount; // The amount of tokens } ``` The `claimant` field encodes both: - **recipient** address (lower 160 bits) - **bytes12 lockTag** (upper 96 bits) This encoding determines processing: 1. **Direct Transfer**: If `lockTag` matches the claimed lock's tag 2. **Convert Lock**: If `lockTag` is non-zero but different 3. **Withdraw**: If `lockTag` is `bytes12(0)` ## IAllocator Interface that allocators must implement. ```solidity interface IAllocator { // Called on standard ERC6909 transfers function attest( address operator, address from, address to, uint256 id, uint256 amount ) external returns (bytes4); // Called during claim processing for on-chain authorization function authorizeClaim( bytes32 claimHash, address arbiter, address sponsor, uint256 nonce, uint256 expires, uint256[2][] calldata idsAndAmounts, bytes calldata allocatorData ) external returns (bytes4); // Off-chain view function function isClaimAuthorized( bytes32 claimHash, address arbiter, address sponsor, uint256 nonce, uint256 expires, uint256[2][] calldata idsAndAmounts, bytes calldata allocatorData ) external view returns (bool); } ``` Return values: - `attest` must return `IAllocator.attest.selector` - `authorizeClaim` must return `IAllocator.authorizeClaim.selector` ## IEmissary Interface for emissaries providing fallback claim verification. ```solidity interface IEmissary { // Called only if all other sponsor verification methods fail function verifyClaim( address sponsor, bytes32 digest, bytes32 claimHash, bytes calldata signature, bytes12 lockTag ) external view returns (bytes4); } ``` Must return `IEmissary.verifyClaim.selector` on successful verification. ## Key Events ```solidity event Claim( address indexed sponsor, address indexed allocator, address indexed arbiter, bytes32 claimHash, uint256 nonce ); event NonceConsumedDirectly( address indexed allocator, uint256 nonce ); event ForcedWithdrawalStatusUpdated( address indexed account, uint256 indexed id, bool activating, uint256 withdrawableAt ); event CompactRegistered( address indexed sponsor, bytes32 claimHash, bytes32 typehash ); event AllocatorRegistered( uint96 allocatorId, address allocator ); event EmissaryAssigned( address indexed sponsor, bytes12 indexed lockTag, address indexed emissary ); event EmissaryAssignmentScheduled( address indexed sponsor, bytes12 indexed lockTag, uint256 assignableAt ); ``` ## Key Errors ```solidity error InvalidToken(address token); error Expired(uint256 expiration); error InvalidSignature(); error PrematureWithdrawal(uint256 id); error ForcedWithdrawalFailed(); error ForcedWithdrawalAlreadyDisabled(address account, uint256 id); error UnallocatedTransfer(address operator, address from, address to, uint256 id, uint256 amount); error InvalidBatchAllocation(); error InvalidRegistrationProof(address allocator); error InvalidBatchDepositStructure(); error AllocatedAmountExceeded(uint256 allocatedAmount, uint256 providedAmount); error InvalidScope(uint256 id); error InvalidDepositTokenOrdering(); error InvalidDepositBalanceChange(); error Permit2CallFailed(); error ReentrantCall(address existingCaller); error InconsistentAllocators(); error InvalidAllocation(address allocator); error ChainIndexOutOfRange(); error InvalidEmissaryAssignment(); error EmissaryAssignmentUnavailable(uint256 assignableAt); error InvalidLockTag(); ``` ## ERC6909 Metadata The Compact implements standard ERC6909 metadata functions: ```solidity function name(uint256 id) external view returns (string memory); function symbol(uint256 id) external view returns (string memory); function decimals(uint256 id) external view returns (uint8); function tokenURI(uint256 id) external view returns (string memory); --- ## Periphery Contracts ## Emissaries Emissaries provide a fallback verification mechanism for sponsors when authorizing claims. This is particularly useful for: 1. Smart contract accounts that might update their EIP-1271 signature verification logic. 2. Accounts using EIP-7702 delegation that leverages EIP-1271. 3. Situations where the sponsor wants to delegate claim verification to a trusted third party. A sponsor assigns an emissary for a specific `lockTag` using [`assignEmissary`](./src/interfaces/ITheCompact.sol#L556). The emissary must implement the [`IEmissary`](./src/interfaces/IEmissary.sol) interface, specifically the `verifyClaim` function. To change an emissary after one has been assigned, the sponsor must first call [`scheduleEmissaryAssignment`](./src/interfaces/ITheCompact.sol#L566), wait for the `resetPeriod` associated with the `lockTag` to elapse, and then call `assignEmissary` again with the new emissary's address (or `address(0)` to remove). ### IEmissary Interface Emissaries must implement the `IEmissary` interface to integrate with The Compact: ```solidity interface IEmissary { function verifyClaim( address sponsor, bytes32 digest, bytes32 claimHash, bytes calldata signature, bytes12 lockTag ) external view returns (bytes4) } ``` **Requirements**: - Must return `IEmissary.verifyClaim.selector` on successful verification ### Assignment and Management #### Assigning an Emissary Sponsors assign emissaries for specific lock tags: ```solidity function assignEmissary( bytes12 lockTag, address emissary ) external ``` **Parameters**: - `lockTag`: The specific resource lock tag this emissary will be authorized for - `emissary`: The address of the emissary contract (or `address(0)` to remove) The emissary assignment is scoped to a specific `lockTag`, meaning sponsors can have different emissaries for different resource locks. #### Changing an Emissary The emissary mechanism includes security timelocks to prevent sudden authorization changes: 1. **Schedule the Change**: Call `scheduleEmissaryAssignment` with the new emissary address 2. **Wait Period**: Wait for the `resetPeriod` associated with the `lockTag` to elapse 3. **Execute Assignment**: Call `assignEmissary` again with the new address ```solidity function scheduleEmissaryAssignment( bytes12 lockTag, address newEmissary ) external ``` #### Removing an Emissary To remove an emissary, assign `address(0)`: ```solidity assignEmissary(lockTag, address(0)) ``` #### Security Rationale This timelock mechanism ensures that emissary changes cannot suddenly alter the authorization logic for outstanding compacts without providing adequate notice. This protects claimants who may have already begun fulfilling compact conditions based on the existing emissary. ### Role in Claim Verification When a claim is submitted for a non-registered compact, The Compact verifies the sponsor's authorization in the following order: 1. **Caller is Sponsor**: If `msg.sender == sponsor`, authorization is granted 2. **ECDSA Signature**: Attempt standard ECDSA signature verification 3. **EIP-1271 `isValidSignature`**: If ECDSA fails, call `isValidSignature` on the sponsor's address (if it's a contract) with half the remaining gas 4. **Emissary `verifyClaim`**: If EIP-1271 fails or isn't applicable, and an emissary is assigned for the sponsor and `lockTag`, call the emissary's `verifyClaim` function ### Events ```solidity event EmissaryAssigned( address indexed sponsor, bytes12 indexed lockTag, address emissary ) ``` Emitted when a sponsor assigns or changes an emissary via `assignEmissary`. ### Trust Assumptions **Sponsors** must trust that emissaries will not authorize claims maliciously, as emissaries effectively have the same authorization power as the sponsor for claim verification. **Claimants** must trust that emissaries (if assigned) will faithfully authorize valid claims. For EIP-7702 sponsors and smart contracts with upgradeable EIP-1271 logic, claimants should require the use of known, canonical emissaries that enforce delays before allowing key rotation. ## Tribunal Tribunal is a framework for processing cross-chain swap settlements against PGA (priority gas auction) blockchains. It ensures that tokens are transferred according to the mandate specified by the originating sponsor and enforces that a single party is able to perform the settlement in the event of a dispute. :::note About Tribunal Tribunal is a reference implementation, not part of The Compact core protocol. The Compact itself is an unopinionated primitive that doesn't depend on or have awareness of any specific settlement engine. Tribunal is included in this documentation as an example that demonstrates how developers can build cross-chain settlement systems on top of The Compact. Other teams can (and should) build their own settlement engines with different trust assumptions, auction mechanisms, or cross-chain messaging approaches. Tribunal simply shows one proven pattern for orchestrating cross-chain fills and settlements using The Compact's resource locks. **Status**: Tribunal is actively under development. For the latest updates and implementation details, see the [Tribunal repository](https://github.com/Uniswap/Tribunal). ::: ### How Tribunal Works Fillers call `fill` and provide any native value necessary to pay for cross-chain messaging. Tribunal verifies expiry, chain IDs, validity conditions, computes hashes and amounts, and then executes the settlement: * **Transfers the filled tokens to the intended recipient** * **For same-chain fills**: Claims tokens via The Compact and calls back into the arbiter or recipient * **For cross-chain fills**: Emits or processes directives that instruct remote arbiters to pull the claim By enforcing a single settlement path, Tribunal eliminates disputes and ensures fairness even in the presence of multiple fillers. ### Extending Tribunal External bridge protocols can extend Tribunal by overriding internal functions to implement the relevant directive processing logic for passing a message to the arbiter on the claim chain (or ensure that the necessary state is updated to allow for the arbiter to "pull" the message themselves). --- ## Resource Locks Resource locks are the fundamental building blocks of The Compact protocol. They are created when a depositor places tokens (either native tokens or ERC20 tokens) into The Compact. ## Structure Each resource lock has four key properties: 1. **Underlying Token**: The token held in the resource lock 2. **Allocator**: Tasked with cosigning on claims against the resource locks 3. **Scope**: Either spendable on any chain or limited to a single chain 4. **Reset Period**: For forcibly exiting the lock and for emissary reassignment timelocks Each unique combination of these four properties is represented by a fungible ERC6909 tokenID. The owner of these ERC6909 tokens can act as a sponsor and create compacts. ## Lock Tag Each resource lock is uniquely identified by a combination of: 1. **Underlying Token**: The address of the ERC20 token or native token held in the lock 2. **Allocator ID**: The ID of the entity responsible for authorizing uses of the lock, which consists of the 4-bit compact flag (shifted left by 88 bits) with the lowest 88 bits of the allocator address, resulting in a 92-bit value 3. **Scope**: Whether the lock can be spent on any chain (Multichain) or only on the same chain where the deposit occurred (Chain-specific) 4. **Reset Period**: The time that must elapse before a forced withdrawal can be completed. The reset period is one of eight predefined values, detailed in the section below. The allocator ID, scope and reset period are encoded into a bytes12 lockTag, and the resource lock's unique ID (the ERC6909 tokenId) is derived by combining this lockTag with the underlying token address. ```solidity lockTag = scope << 255 | resetPeriod << 252 | allocatorId << 160 lockId = lockTag | tokenAddress ``` ### Allocator ID Details The compact flag is a 4-bit value (0-15) that represents how "compact" an allocator address is, based on the number of leading zero nibbles: - If address has 0-3 leading zero nibbles: flag = 0 - If address has 4-17 leading zero nibbles: flag = (number of leading zeros - 3) - If address has 18+ leading zero nibbles: flag = 15 Mathematically, the allocator ID can be represented as: ```solidity compactFlag = min(max(0, leadingZeroNibbles - 3), 15) id = (compactFlag << 88) | (allocator & 0x00000000000000000000FFFFFFFFFFFFFFFFFFFF) ``` ### Reset Period Details The reset period is one of eight predefined values: | Index | Reset Period Value | Index | Reset Period Value | |-------|---------------------------|-------|---------------------------| | 0 | `OneSecond` | 4 | `OneHourAndFiveMinutes` | | 1 | `FifteenSeconds` | 5 | `OneDay` | | 2 | `OneMinute` | 6 | `SevenDaysAndOneHour` | | 3 | `TenMinutes` | 7 | `ThirtyDays` | ## Creating Resource Locks Resource locks are created by depositing tokens into The Compact. Multiple deposit methods are available: ### Native Token Deposits ```solidity function depositNative( address recipient, bytes12 lockTag ) external payable returns (uint256 id) ``` ### ERC20 Token Deposits ```solidity function depositERC20( address token, address recipient, uint256 amount, bytes12 lockTag ) external returns (uint256 id) ``` ### Batch Deposits ```solidity function batchDeposit( DepositDetails[] calldata depositDetails ) external payable returns (uint256[] memory ids) ``` ### Permit2 Integration ```solidity function depositERC20ViaPermit2( ISignatureTransfer.PermitBatchTransferFrom memory permit, bytes memory signature ) external returns (uint256 id) ``` ## Token Handling ### Native Tokens For native tokens, The Compact mints an amount of ERC6909 tokens equal to the msg.value. For example, a native deposit with a value of 1e18 wei would result in exactly 1e18 ERC6909 tokens being minted to the caller (or specified recipient). A withdrawal of native underlying tokens from a resource lock causes a native value equal to the number of burned ERC6909 tokens to be transferred out of the contract. ### ERC20 Tokens For ERC20 tokens, The Compact mints an amount of ERC6909 tokens equal to the actual balance change observed by the protocol as a result of an ERC20 deposit, accounting for the token's precision. In most cases, this is equal to the `amount` parameter passed to the token's `transfer`/`transferFrom` function, which is analogous to `msg.value`. One notable exception is fee-on-transfer tokens, where the actual and intended balance changes differ. Deposits and withdrawals against an ERC20 resource lock are handled by: - Checking the contract's balance before and after the token transfer - Minting (or burning) an amount of ERC6909 tokens exactly equal to the observed balance change ### Fee-on-Transfer Tokens The Compact correctly handles fee-on-transfer tokens for both deposits and withdrawals. The amount of ERC6909 tokens minted or burned is based on the *actual balance change* in The Compact contract, not just the specified amount. :::warning Important Integration Notice In order to support fee-on-transfer tokens, The Compact does not fully adhere to the Checks-Effects-Interactions (CEI) paradigm as part of deposits and withdrawals. If you are integrating with The Compact, particularly as an allocator, and are reading ERC6909 token balances, be aware that those balances may subsequently increase or decrease as part of the same transaction if the call executes a deposit, withdrawal, or claim against The Compact that in turn triggers a nested call to the integrator in question. To ensure that all balances are fully "settled," integrators should first ensure that the reentrancy guard on The Compact is not set via: ```solidity require( TheCompact.exttload(0x0000000000000000000000000000000000000000000000929eee149b4bd21268) < 2, "Balance state may not be final on The Compact" ) ``` *Note: Use `extsload` instead of `exttload` on chains without `tstore` support.* ::: ### Rebasing Tokens **Rebasing tokens (e.g., stETH) are NOT supported in The Compact V1.** Any yield or other balance changes occurring after deposit will not accrue to the depositor's ERC6909 tokens. For such assets, use their wrapped, non-rebasing counterparts (e.g., wstETH). ## Forced Withdrawals Resource lock owners can initiate forced withdrawals if an allocator becomes unresponsive: 1. **Enable**: Call `enableForcedWithdrawal(uint256 id)` 2. **Wait**: The `resetPeriod` must elapse 3. **Withdraw**: Call `forcedWithdrawal(uint256 id, address recipient, uint256 amount)` The forced withdrawal state can be reversed with `disableForcedWithdrawal(uint256 id)`. ## View Functions Query resource lock details: ```solidity function getLockDetails(uint256 id) external view returns ( address token, address allocator, uint48 resetPeriod, Scope scope, bytes12 lockTag ) ``` Check forced withdrawal status: ```solidity function getForcedWithdrawalStatus( address account, uint256 id ) external view returns (ForcedWithdrawalStatus status) ``` ## Error Handling Common errors when working with resource locks: - `InvalidToken(address token)`: Invalid token address provided - `InvalidLockTag()`: Invalid lock tag provided - `InvalidDepositBalanceChange()`: Actual balance change doesn't match expectations - `PrematureWithdrawal(uint256 id)`: Attempting withdrawal before reset period - `ForcedWithdrawalFailed()`: Forced withdrawal operation failed --- ## Resources(The-compact) ## Core Contracts The foundation of The Compact protocol - an ERC6909-based system for reusable resource locks. - **[TheCompact](https://github.com/Uniswap/the-compact)** - Main protocol implementation - **[ITheCompact Interface](https://github.com/Uniswap/the-compact/blob/main/src/interfaces/ITheCompact.sol)** - Core interface definition - **[ITheCompactClaims Interface](https://github.com/Uniswap/the-compact/blob/main/src/interfaces/ITheCompactClaims.sol)** - Claims interface - **[License](https://github.com/Uniswap/the-compact/blob/main/LICENSE.md)** - MIT ## Peripheral Contracts :::warning DEVELOPER WARNING These repositories are under development and are intended to serve as reference implementations for understanding peripheral contract functionality within The Compact. These are for testing/reference purposes only - do not use these contracts in production environments. ::: ### Reference Allocators #### Smart Contract Allocators **[Repository](https://github.com/Uniswap/sc-allocators)** Various example allocator implementations for use with The Compact. Allocators co-sign or authorize claims against sponsors' locked balances, prevent under-allocation, and in the case of the HybridERC7683 implementation, broadcast cross-chain orders using ERC-7683. Some allocators also rely on Uniswap Tribunal. The provided examples include both fully on-chain and hybrid (on-chain + off-chain) allocators. #### Smallocator (Off-chain) **[Repository](https://github.com/uniswap/smallocator)** A minimalistic server-based allocator for The Compact. Smallocator provides an API for sponsors to request resource lock allocations across multiple blockchains, with support for EIP-4361 session authentication and signing EIP-712 Compact messages. It also includes a frontend application for interacting directly with the server that also facilitates making deposits into resource locks it oversees. #### Autocator (Off-chain) **[Repository](https://github.com/uniswap/autocator)** A server-based allocator for The Compact that leverages protocol signatures and transactions for authentication. Autocator provides an API for requesting resource lock allocations across multiple blockchains by providing the details of associated compacts with accompanying sponsor signatures or onchain registrations. It also includes a frontend application for interacting directly with the server that also facilitates making deposits into resource locks it oversees. ### Supporting Infrastructure #### Emissary **[Repository](https://github.com/Uniswap/emissary)** An on-chain signature authority delegation registry with support for secp256k1 and P256 keys (both HSM and WebAuthn formats). Designed for use as a fallback signature validation mechanism. #### Tribunal **[Repository](https://github.com/Uniswap/Tribunal)** Tribunal is an illustration for how processing cross-chain swap settlements against PGA (priority gas auction) blockchains. It ensures that tokens are transferred according to the mandate specified by the originating sponsor and enforces that a single party is able to perform the settlement in the event of a dispute. #### Arbiters **[Repository](http://github.com/Uniswap/arbiters)** A repository for developing arbiter implementations that leverage The Compact for performing cross-chain swaps. --- ## PoolManager In Uniswap v3, each liquidity pool was represented by a separate smart contract deployed through the Uniswapv3Factory contract. While this approach provided flexibility, it also led to increased gas costs for pool creation and multi-hop swaps. Uniswap v4 addresses this issue by introducing the Singleton design pattern. The PoolManager contract now serves as a single entry point for all liquidity pools. Instead of deploying separate contracts for each pool, the pool state and logic are encapsulated within the PoolManager itself. ## Purpose The primary purpose of the `PoolManager` is to: - Efficiently manage liquidity pools - Facilitate token swaps - Reduce gas costs compared to the factory-based approach in Uniswap v3 - Enable extensibility through hooks ## Architecture ### Singleton Design - Uniswap v4 uses a Singleton design pattern for the `PoolManager` - All pool state and logic are encapsulated within the `PoolManager` contract ### Locking Mechanism - The `PoolManager` uses a locking mechanism to allow for _flash accounting_ (also known as deferred balance accounting) - When unlocked, the calling contract can perform various operations and zero-out outstanding balances before returning control to the `PoolManager` for final solvency checks ### Pool State - The `Pool.State` struct contains information such as: - Current price - Liquidity - Tick bitmap - Fee growth - Position information ### Libraries - The pool logic is implemented using Solidity libraries to keep the `PoolManager` contract modular and gas-efficient - These libraries are: - `Pool`: Contains core pool functionality, such as swaps and liquidity management - `Hooks`: Handles the execution of hook functions - `Position`: Manages liquidity positions within a pool ## Core Functionality ### Pool Creation - New pools are created by calling the `initialize` function on the `PoolManager` - The pool creator specifies the token pair, fee tier, tick spacing, and optional hook contract address - The `PoolManager` initializes the pool state and associates it with a unique `PoolId` ### Swaps - Swaps are initiated through the `swap` function on the `PoolManager`, typically via a swap router contract - The `PoolManager` executes the following steps: 1. Checks if the pool is valid and initialized 2. Executes the `beforeSwap` hook, if applicable 3. Performs the actual swap, updating the pool state and charging fees 4. Executes the `afterSwap` hook, if applicable 5. Calculates the net token amounts owed to the user and the pool, represented by the `BalanceDelta` struct - Swaps utilize flash accounting, where tokens are moved into the `PoolManager`, and only the final output tokens are withdrawn ### Liquidity Management - Liquidity providers can add or remove liquidity using the `modifyLiquidity` function on the `PoolManager`. However, you wouldn't call this directly from your application, you would call this from a periphery contract to handle the locking & unlocking a particular pool. - The `PoolManager` executes the following steps: 1. Checks if the pool is valid and initialized 2. Determines if the modification is an addition or removal of liquidity 3. Executes the appropriate `beforeAddLiquidity` or `beforeRemoveLiquidity` hook, if applicable 4. Performs the actual liquidity modification and updates the pool state 5. Emits the `ModifyLiquidity` event 6. Executes the appropriate `afterAddLiquidity` or `afterRemoveLiquidity` hook, if applicable 7. Calculates the balance delta and returns it to the caller ### Flash Accounting - The `PoolManager` employs flash accounting to reduce gas costs and simplify multi-hop swaps - Tokens are moved into the `PoolManager` contract, and all subsequent actions are performed within the contract's context - Only the final output tokens are withdrawn from the `PoolManager` at the end of the transaction ### Transient Storage - The `PoolManager` utilizes transient storage (EIP-1153) to store temporary data during complex operations - Transient storage reduces gas costs by avoiding regular storage operations for data only needed within a single transaction --- ## Dynamic Fees Uniswap v4 introduces dynamic fees, allowing for flexible and responsive fee structures managed through hooks. This feature enables pools to adapt fees to changing market conditions, potentially improving liquidity provider profitability and overall market efficiency. ## What are Dynamic Fees? Dynamic fees in Uniswap v4 are a specific type of swap fee paid by swappers that directly accrue to liquidity providers. These fees are distinct from protocol fees and hook fees (Optional fees that can be implemented by custom hooks), and represent a significant advancement over the fee structures in previous Uniswap versions. Unlike the static fee tiers in Uniswap v3 (0.05%, 0.30%, 1.0%) or the single fee in v2, dynamic fees in v4 offer much more flexibility. Dynamic fees can: - Adjust in real-time based on various market conditions - Change on a per-swap basis - Allow for any fee percentage (e.g., 4.9 bips, 10 bips) - Be updated at various intervals (yearly, per block, or per transaction) This dynamic nature allows for more efficient fee pricing, potentially benefiting both liquidity providers and traders by adapting to current market conditions. By allowing fees to fluctuate based on market dynamics, Uniswap v4 aims to optimize liquidity provision and trading across a wide range of market scenarios. ## Motivation and Benefits of Dynamic Fees 1. **Improved Pricing of Volatility:** Adapt fees to market volatility, similar to traditional exchanges adjusting bid-ask spreads. 2. **Order Flow Discrimination:** Price different types of trades (e.g., arbitrage vs. uninformed) more accurately. 3. **Improved Market Efficiency and Stability:** Fees can adjust to reflect real-time market conditions, optimizing for both liquidity providers and traders. Dynamic fees could help dampen extreme market movements by adjusting incentives in real-time. 4. **Enhanced Capital Efficiency and Liquidity Provider Returns:** By optimizing fees, pools can attract more liquidity and facilitate more efficient trading. More accurate fee pricing could lead to better returns for liquidity providers, potentially attracting more capital to pools. 5. **Better Risk Management:** During high volatility, fees can increase to protect liquidity providers from impermanent loss. 6. **Customizable Strategies:** Enable complex fee strategies for specific token pairs or market segments. ## Dynamic Fees Use Cases 1. **Volatility-Based Fees:** Adjust fees based on the historical or realized volatility of the asset pair. 2. **Volume-Based Fees:** Lower fees during high-volume periods to attract more trades, and increase fees during low-volume periods to compensate liquidity providers. 3. **Time-Based Fees:** Implement different fee structures for different times of day or days of the week, based on historical trading patterns. 4. **Market Depth-Based Fees:** Adjust fees based on the current liquidity depth in the pool. 5. **Cross-Pool Arbitrage Mitigation:** Dynamically adjust fees to discourage harmful arbitrage between different pools or exchanges. 6. **Gas Price-Responsive Fees:** Adjust fees based on network congestion and gas prices to ensure profitability for liquidity providers. 7. **Event-Driven Fees:** Implement special fee structures during significant market events or token-specific occurrences. 8. **Lookback approach:** Set the fee to match the most profitable fee tier of external pools with the same asset pair over a recent period. 9. **Price oracle approach:** Use an external price oracle to determine the correct asset price and adjust fees based on how trades move the pool price relative to this external price. 10. **Price momentum approach:** Analyze recent price history and asymmetrically adjust fees based on trade direction. 11. **Asset composition approach:** Lower fees for trades that balance the pool and higher fees for trades that imbalance it. 12. **Transaction-source based approach:** Provide lower fees for transactions routed through certain aggregators or sources less likely to be arbitrage trades. ## Dynamic Fees Mechanism In Uniswap v4, the dynamic fee capability of a pool is determined at pool creation and is immutable. This means that whether a pool uses dynamic fees or not is set when the pool is initially created and cannot be changed afterwards. For pools that do use dynamic fees, Uniswap v4 supports two primary methods for updating the fee: 1. **Periodic Updates via PoolManager:** Fees can be updated by calling the `updateDynamicLPFee` function on the PoolManager contract at specified intervals. 2. **Per-Swap Updates via beforeSwap Hook:** Fees can be dynamically set for each swap by returning the fee from the `beforeSwap` hook. This allows hooks to override the LP fee for each swap in dynamic fee pools. These methods offer flexibility in implementing various fee strategies. Below is an example of how to set dynamic fees using the `beforeSwap` hook: ![](./images/Uniswap_V4_Dynamic_Fees_Step_1.jpg) Before a swap occurs, the `beforeSwap` hook is invoked to determine the fee based on the defined logic. This hook calls the `updateDynamicLPFee` function on the PoolManager contract to update the fee. ![](./images/Uniswap_V4_Dynamic_Fees_Step_2.jpg) The `updateDynamicLPFee` function in the PoolManager contract updates the pool's fee accordingly. For more detailed information on implementing these methods, please refer to our [Dynamic Fees Implementation Guide](https://uniswap-docs-staging.vercel.app/documentation/featured-guides/hooks/v4/guides-for-solidity-contracts/dynamic-fee-pools). ## Considerations and Best Practices - The optimal fee depends on at least two factors: **asset volatility** and **volume of uninformed flow.** - For volatile pairs in systems like Uniswap v3, which don't discriminate between flows, low fee-tier pools are only sensible when uninformed flow is large and asset volatility is relatively low. - Performance implications of frequent fee updates should be carefully considered. - Security measures should be implemented to prevent manipulation of fee-setting mechanisms. - Balance responsiveness with gas costs to optimize for both performance and cost-effectiveness. For more detailed implementation guidance and best practices, refer to our [Dynamic Fees Implementation Guide](https://uniswap-docs-staging.vercel.app/documentation/featured-guides/hooks/v4/guides-for-solidity-contracts/dynamic-fee-pools). --- ## ERC-6909 Uniswap v4 uses [ERC-6909](https://eips.ethereum.org/EIPS/eip-6909) to further improve gas-efficiency on token claims and redemptions. ERC-6909 is a minimal and gas-efficient standard for managing multiple ERC-20 tokens from a single contract. It provides a simplified alternative to the more complex ERC-1155 multi-token standard. ### ERC-6909 vs ERC-1155 ERC-6909 offers several advantages over ERC-1155: 1. Simplified interface: ERC-6909 removes unnecessary safe transfer callbacks and batching constraints presented in ERC-1155. 2. Improved transfer delegation: ERC-6909 provides a more efficient system for transfer delegation. 3. Gas efficiency: ERC-6909 reduces gas costs for deployment, transfers, and burning operations. 4. Reduced code size: Implementing ERC-6909 results in smaller contract sizes compared to ERC-1155. However, it's worth noting that ERC-6909 does introduce a `totalSupply` variable, which leads to an additional disk write on mint and burn operations. ## How it works Instead of choosing to move tokens in/out of the `PoolManager`, developers can opt-in and leave the ERC-20 tokens within the `PoolManager`. In exchange, the `PoolManager` can **mint them an ERC-6909 token representing their claim**. In subsequent interactions requiring _paying_ tokens, users will not need to transfer ERC-20 tokens into the `PoolManager` - users can simply _burn_ some (or all) of their claim tokens they have Doing _real_ ERC-20 token transfers requires calls to external smart contracts - incurring gas overhead compared to internal accounting. Secondly, these external smart contracts have their own custom logic within their `transfer` functions - for example USDC's blocked-address list - which is a further gas overhead. Thus, minting and burning ERC-6909 tokens are more gas-efficient because they don't require external function calls and have a constant-size gas overhead regardless of the underlying ERC-20 token. This mechanism therefore helps further reduce gas costs. All these gas cost reductions overall make pools much more competitive based on the fees they charge. ## Examples ### High-frequency traders / MEV bots These users are often conducting a lot of swaps in relatively short durations of time, while staying within the Uniswap Protocol. These power-users can trade using ERC-6909 tokens for improved gas-efficiency. ### Liquidity management ERC-6909 does not only benefit swappers. For power-users that may be opening and closing liquidity positions frequently, liquidity managers can opt-in and receive their capital as ERC-6909. --- ## v4 Fee Structure Guide ## Overview of Fee Types In Uniswap v4, there are three main types of fees to understand: - **LP Fee**: Fees earned by liquidity providers - **Protocol Fee**: Fees collected by the protocol - **Swap Fee**: Total fee paid by swappers (calculated by applying protocol fee and LP fee sequentially) --- ## LP Fees LP fees are set by the pool initializer at pool creation and may be static or dynamic. **Fee Range:** - Maximum LP Fee: 100% - Minimum LP Fee: 0% - **Granularity**: Fees are set at pip-level precision ### Static LP Fees - **Immutable** once set during pool initialization - **Unlimited fee options** in v4 (major improvement from v3) - In **v3**, LP fee options were limited to: 0.01%, 0.05%, 0.30%, and 1.00% ### Dynamic LP Fees Dynamic fees offer more flexibility and real-time adjustability: - A dynamic fee pool signals this capability by setting its LP fee to `0x800000` (where the first bit = 1) - **Only the pool's hook** can update the dynamic fee—no additional permissions required - A hook **cannot** update fees if the pool's fee is not set to `0x800000` --- ## Protocol Fees Protocol fees are configured **per pool** with the following characteristics: - Controlled by the **protocol fee controller** (set by the pool manager owner) - **Maximum protocol fee**: 0.1% (1,000 pips) - **Granularity**: Fees are set at pip-level precision (not basis points) - **Unit conversion**: 1 basis point = 100 pips - **Directional fees**: Separate fees can be set for: - token0 → token1 swaps - token1 → token0 swaps --- ## Swap Fees ### Key Change from v3 to v4 **v3 behavior**: Swap fee = LP fee (protocol fee was a percentage taken from LP fees) **v4 behavior**: Swap fee = effective total fee after applying both protocol and LP fees sequentially ### Application Order 1. **Protocol fee** applied first to the input amount 2. **LP fee** applied second to the remaining input (after protocol fee deduction) **Impact on LP Earnings:** Note that this sequential application means introducing or increasing protocol fees will reduce LP earnings even if swap volume remains constant, since LPs now earn fees on a smaller base amount. ### Fee Cap - **Total swap fee capped at 100%** of input amount - **Important**: If swap fee = 100%, exact output swaps become impossible (entire input consumed by fees) ### Fee Calculation Formula ```solidity // Method 1: Sequential application uint256 swapFee = protocolFee + (lpFee * (1_000_000 - protocolFee)) / 1_000_000; (rounded up) // Method 2: Mathematically equivalent uint256 swapFee = protocolFee + lpFee - (protocolFee * lpFee) / 1_000_000; ``` ### Mathematical Derivation Starting with input amount: ``` amountIn ``` **Step 1**: Protocol fee takes: ``` amountIn × (protocolFee / 1_000_000) ``` **Step 2**: Remaining after protocol fee: ``` amountIn × (1 - protocolFee / 1_000_000) ``` **Step 3**: LP fee applies to remaining: ``` lpFee × (remaining amount) ``` **Final formula**: ``` swapFee = protocolFee + (lpFee × (1 - protocolFee / 1_000_000)) ``` Which simplifies to: ``` swapFee = protocolFee + lpFee - (protocolFee × lpFee) / 1_000_000 ``` ## Example Calculation **Given:** - `protocolFee = 50` pips → 0.005% - `lpFee = 3000` pips → 0.30% **Calculation:** ```solidity swapFee = 50 + 3000 - (50 × 3000) / 1_000_000 = 50 + 3000 - 150 / 1_000_000 = 50 + 3000 - 0.15 = 3049.85 pips ``` **Result:** 3049.85 pips = **0.304985%** total swap fee --- ## Key Takeaways - **Sequential application**: Protocol fees are deducted first, then LP fees apply to the remainder - **Dynamic flexibility**: v4 introduces unlimited static fee tiers and dynamic fee capabilities - **Directional control**: Protocol fees can differ by swap direction - **Fee interaction**: The combined effect is slightly less than simple addition due to sequential application --- ## Flash Accounting In previous versions of Uniswap, every time a swap was made - including multi-hop swap - tokens were transferred between Pool contracts for intermediate steps. This design incurred inefficiencies because transferring tokens with external calls to their smart contracts - especially in a multi-hop swap - is quite expensive. This design was required since each pool was its own contract and token transfers were required to maintain accounting and solvency. With the singleton architecture, a better design was possible and is referred to as _Flash Accounting_. The design became practical with gas efficiencies of [Transient Storage](https://eips.ethereum.org/EIPS/eip-1153). _Flash Accounting_ further reduces the gas cost of trades that cross multiple pools and supports more complex integrations with Uniswap v4. With _flash accounting_, each balance-changing operation (e.g. swap and liquidity modification) updates an internal net balance known as `delta`. Only the final balance-changes require token transfers. ![Flash Accounting Step 1](./images/Uniswap_V4_Flash_Accounting_Step_1.png) ![Flash Accounting Step 2](./images/Uniswap_V4_Flash_Accounting_Step_2.png) For example from the above diagrams - let's say you have 20 `USDC` and 20 `USDT` but you want to add liquidity with 15 `USDC` and 25 `USDT`. Previously this would require multiple external calls as tokens were transferred between Pool contracts when swapping from `USDC` to `USDT`. But now with v4's Flash Accounting we only need to keep track of `delta` - thus we can `swap` and `modifyLiquidity` in a single call and only the final balance-changes involve actual token transfers. In the above example, we are swapping 5 USDC to 5 USDT to create liquidity with 15 USDC and 25 USDT. In between the operations (*swap, modifyLiquidity*), no token transfers are made ## Mechanism ### Locking To ensure correctness and atomicity in complex operations like a multi-hop swap - v4 uses a locking mechanism. Anytime key actions need to take place within the `PoolManager` - e.g. swaps and liquidity modification - a periphery contract must `unlock` the `PoolManager` first. Then integrators implement the `unlockCallback` and proceed with any of the following actions on the pools: - swap - modifyLiquidity - donate - take - settle - mint - burn - sync *Note that pool initialization can happen outside the context of unlocking the `PoolManager`, as there are no balance-changing operations associated with pool creation.* The following diagrams visualize how the above steps will be implemented: 1. `unlock` the `PoolManager` ![Flash Accounting Locking Mechanism Step 1](./images/Uniswap_V4_Locking_Mechanism_Step_1.png) 2. Implement `unlockCallback` and proceed with any desired pool actions ![Flash Accounting Locking Mechanism Step 2](./images/Uniswap_V4_Locking_Mechanism_Step_2.png) 3. The actual token transfer happens at the end and the *delta* should be resolved ![Flash Accounting Locking Mechanism Step 3](./images/Uniswap_V4_Locking_Mechanism_Step_3.png) ### Balance Delta Inside `unlockCallback`, a periphery contract performs balance-changing operations i.e. conduct swaps, modify positions, etc. After returning execution context back to `PoolManager`, the core contract checks that balances are resolved - nothing is owed to or from the `PoolManager`. The balances resolved above is what we refer as the `delta`, a field held in the _transient_ state. The value(s) represent the _debts_ and _credits_ of assets owed to or from the `PoolManager`. ## Swapping ![Multi-hop swaps on V3 vs V4](./images/Uniswap_V4_Multihop_Swaps.png) As shown in the above diagram, for example - let's say you want to swap `ETH` for `DAI`. Assuming this requires a multi-hop swap going from `ETH` to `USDC` and then from `USDC` to `DAI`. ### Previously on v3 1. `ETH` is transferred to `ETH <> USDC` pool contract 2. `USDC` is withdrawn from `ETH <> USDC` contract and transferred to `USDC <> DAI` contract 3. `DAI` is withdrawn from `USDC <> DAI` contract and transferred to the user ### Now on v4 1. Call `swap()` on `ETH <> USDC` 2. Call `swap()` on `USDC <> DAI`, with the credit of USDC from above being used as the input amount 3. User _resolves deltas_ by paying ETH and receiving DAI Therefore we can skip the step of actually calling `transfer()` on the USDC contract. The optimization scales infinitely, any number of arbitrary hops only requires two token transfers - input and output tokens. ## Liquidity Management The optimization becomes more evident for complex liquidity operations For example, a user wanted to add liquidity to `ETH <> DAI` but does not have DAI. The user can swap some `ETH` to `DAI` in order to add liquidity with both tokens. In addition, the user can multi-hop swap going from `ETH` to `USDC` to `DAI`. If properly integrated, the user would only need to transfer ETH _once_. ## Developer Resources To see how unlock callback and delta work in a smart contract read [Unlock Callback & Deltas](/contracts/v4/guides/unlock-callback). --- ## Hooks Uniswap v4 introduces Hooks, a system that allows developers to customize and extend the behavior of liquidity pools. Hooks are external smart contracts that can be attached to individual pools. Every pool can have one hook but a hook can serve an infinite amount of pools to intercept and modify the execution flow at specific points during pool-related actions. ## Key Concepts ### Pool-Specific Hooks - Each liquidity pool in Uniswap v4 can have its own hook contract attached to it. Hooks are optional for Uniswap v4 pools. - The hook contract is specified when creating a new pool in the `PoolManager.initialize` function. - Having pool-specific hooks allows for fine-grained control and customization of individual pools. ## Core Hook Functions Uniswap v4 provides a set of core hook functions that can be implemented by developers. Developers do not have to implement every hook, you can mix&match them to whatever your liking is. You can use one or all of them! - Hook contracts specify the permissions that determine which hook functions they implement, which is encoded in the address of the contract. - The `PoolManager` uses these permissions to determine which hook functions to call for a given pool based on its Key. ### Initialize Hooks - `beforeInitialize`: Called before a new pool is initialized. - `afterInitialize`: Called after a new pool is initialized. - These hooks allow developers to perform custom actions or validations during pool initialization, but these hooks can only be invoked once. ### Liquidity Modification Hooks The liquidity modification hooks are extremely granular for security purposes. - `beforeAddLiquidity`: Called before liquidity is added to a pool. - `afterAddLiquidity`: Called after liquidity is added to a pool. - `beforeRemoveLiquidity`: Called before liquidity is removed from a pool. - `afterRemoveLiquidity`: Called after liquidity is removed from a pool. ### Swap Hooks - `beforeSwap`: Called before a swap is executed in a pool. - `afterSwap`: Called after a swap is executed in a pool. ### Donate Hooks - `beforeDonate`: Called before a donation is made to a pool. - `afterDonate`: Called after a donation is made to a pool. - Donate hooks provide a way to customize the behavior of token donations to liquidity providers. ## Innovation and Potential The introduction of hooks in Uniswap v4 opens up a world of possibilities for developers to innovate and build new DeFi protocols. Some potential use cases include: - Customized AMMs with different pricing curves than xy = k. - Yield farming and liquidity mining protocols that incentivize liquidity provision. - Derivative and synthetic asset platforms built on top of Uniswap v4 liquidity. - Lending hooks integrated with Uniswap v4 pools. As a hook developer you can easily bootstrap the codebase of an entirely new DeFi protocol through hook designs, which subsequently drives down your audit costs and allows you to develop faster. However, it's important to note that just because you made a hook, that does not mean you will get liquidity routed to your hook from the Uniswap frontend. --- ## Integrated Routing with UniswapX The [Uniswap Interface](https://app.uniswap.org) will be ramping up support for hooks in its standard routing system progressively over time. Hook builders looking to get immediate access to flow from the interface can do so by running a UniswapX filler for their hooked pools. At a high level, hook builders' filler implementations will need to do the following: 1. (On Mainnet) Subscribe to the UniswapX RFQ system and submit fillable bids from orders they receive 2. Listen to the public feed for orders they won or that are open to be filled publicly 3. Execute those orders against pools that use their hooks Developers should check [UniswapX Documentation](/contracts/uniswapx/overview) to get started. --- ## Security When building on Uniswap v4, security should be a primary consideration. This section covers emergency response resources and security best practices specific to v4 implementations. ## Emergency Response ### SEAL 911 Emergency Hotline If you encounter a security incident (exploit, vulnerability, or other urgent security matter) while working with Uniswap v4, the SEAL 911 Emergency Hotline provides immediate access to security experts. **Emergency Contact**: https://t.me/seal_911_bot SEAL 911 is a community-operated Telegram bot that connects you directly with vetted security responders who can provide immediate assistance during security incidents. #### How It Works - Send a message through the bot during a security emergency - Automatic alert routing to a vetted group of white hat security professionals - Immediate response from trusted security experts in the space #### Additional Resources - [SEAL 911 GitHub Repository](https://github.com/security-alliance/seal-911) - [Security Alliance Website](https://www.securityalliance.org/seal-911) :::note SEAL 911 is a third-party service operated by the Security Alliance. Exercise appropriate judgment when sharing sensitive information during emergency situations. ::: ## v4-Specific Security Considerations ### Hook Security When developing custom hooks for v4, ensure proper validation and access controls. Malicious or poorly implemented hooks can compromise pool security. ### Flash Accounting v4's flash accounting system requires careful implementation to prevent exploitation. Always ensure proper settlement of deltas. ### Pool Manager Interactions Direct interactions with the PoolManager require thorough understanding of the locking mechanism and callback patterns. ## Audits Uniswap's V4 core contracts have undergone a handful of extensive security reviews by multiple providers, with some reviews still ongoing. Below is a list of completed and draft reports. The full list can be found in the respective repositories' [audits directory](https://github.com/Uniswap/v4-core/blob/main/docs/security/audits): > [Open Zeppelin report](https://github.com/Uniswap/v4-core/blob/main/docs/security/audits/OpenZeppelin_audit_core.pdf) from July 17th 2024. > [Certora draft report](https://github.com/Uniswap/v4-core/blob/main/docs/security/audits/DRAFT_Certora_audit_core.pdf) from July 2024. > [Trail of Bits report](https://github.com/Uniswap/v4-core/blob/main/docs/security/audits/TrailOfBits_audit_core.pdf) from September 5th 2024. > [Spearbit draft report](https://github.com/Uniswap/v4-core/blob/main/docs/security/audits/DRAFT_Spearbit_audit_core.pdf) from September 5th 2024. > [ABDK draft report](https://github.com/Uniswap/v4-core/blob/main/docs/security/audits/DRAFT_ABDK_audit_core.pdf) from September 5th 2024. Similarly, the V4 periphery contracts have been reviewed by various audit providers, and the full list is inside the periphery repository's [audits directory](https://github.com/Uniswap/v4-periphery/tree/main/audits): > [Open Zeppelin report](https://github.com/Uniswap/v4-periphery/blob/main/audits/OpenZeppelin_audit_periphery_universal_router.pdf) from September 5th 2024. > [Spearbit draft report](https://github.com/Uniswap/v4-periphery/blob/main/audits/DRAFT_Spearbit_audit_periphery.pdf) from September 5th 2024. > [ABDK draft report](https://github.com/Uniswap/v4-periphery/blob/main/audits/DRAFT_ABDK_audit_periphery_universal_router.pdf) from September 5th 2024. ## Bug Bounty Program In November 2024 Uniswap announced a [$15.5 million dollar bug bounty](https://blog.uniswap.org/v4-bug-bounty) for their V4 contracts. You can view the full [bounty page](https://cantina.xyz/bounties/f9df94db-c7b1-434b-bb06-d1360abdd1be) on Cantina. ## Additional Security Resources - Review the [v4 Core contracts](../reference/core/) for implementation details - Follow security best practices outlined in the [Hooks documentation](./04-hooks.mdx) - Test thoroughly using the provided [test contracts](../reference/core/test/) --- ## Subscribers Subscribers, new in Uniswap v4, allow for liquidity-position owners to opt-in to a contract that receives _notifications_. The new design is intended to support _liquidity mining_, additional rewards given to in-range liquidity providers. Through notification logic, position owners do not need to risk their liquidity position and its underlying assets. In Uniswap v3, _liquidity mining_ was supported by fully transferring the liquidity position to an external contract; this old design would give the external contract full ownership and control of the liquidity position. When a position owner _subscribes_ to a contract, the contract will receive notifcations when: * The position is initially subscribed * The position increases or decreases its liquidity * The position is transferred * The position is unsubscribed --- ## v4 vs v3 While Uniswap v4's underlying concentrated liquidity is the same as Uniswap v3, there are some key differences in the architecture and accounting. ## Singleton Design ### Pool Creation **v4**: The singleton contract facilitates the creation of a pool and also stores its state. This pattern reduces costs when creating a pool and doing multi-hop swaps. Because pools are _contract state_ and not entirely new _contracts_ themselves, pool creation is significantly cheaper. **v3**: A factory contract is responsible for pool creation. The pool is a separate contract instance that manages its own state. Pool initialization is costly because contract creation is gas-intensive ### Flash Accounting **v4**: The singleton uses _flash accounting_, meaning a caller that unlocks the PoolManager is allowed to cause balance-changing operations (multiple swaps, multiple liquidity modifications, etc) and only needs to perform token transfers at the very end of the sequence. **v3**: Because flash accounting is missing from v3, it is the responsibility of the integrating contract to perform token transfers, after each individual call, to each individual pool contract ## Liquidity Fee Accounting **v4**: Accrued fees act like a credit when modifying liquidity. Increasing liquidity will convert the fee revenue to liquidity inside the position while decreasing liquidity will automatically require the withdrawal of unclaimed fee revenue. An additional parameter _salt_ can be provided when creating liquidity. The _salt_ is used to distinguish positions of the same range on the same pool. This separation may be preferred to simplify fee accounting. If two users share the same range and state in `PoolManager`, integrating contracts must be careful in managing fees **v3**: Liquidity positions of the same range and pool will share the same state. While believed to be more gas efficient at the time, integrating contracts will need to handle fee management since the state is shared on the core pool contract ## Native ETH **v4**: Pool pairs support native tokens, in doing so ETH swappers and liquidity providers benefit from gas cost reductions from cheaper transfers and removal of additional wrapping costs. **v3**: ETH needs to be wrapped first before being paired with other tokens. This results in higher gas costs because of wrapping and transferring a wrapped native token. ## Subscribers Only v4: Owners can now set a subscriber for their positions. A subscriber contract will get notified every time the position's liquidity or owner changes. Subscribers enable staking / liquidity-mining, but users do not need to transfer their ERC-721 token. **v3**: Staking in v3 requires users to transfer their ERC-721 token to a contract, putting the underlying assets at risk for malicious behavior. --- ## Deployments(V4) The Uniswap Protocol is made up of multiple contracts on many networks. The latest version of `@uniswap/v4-core`, `@uniswap/v4-periphery`, and `@uniswap/universal-router` are deployed at the addresses listed below. Integrators should **no longer assume that they are deployed to the same addresses across chains** and be extremely careful to confirm mappings below. ## Mainnet Deployments ### Ethereum: 1 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x000000000004444c5dc75cB358380D2e3dE08A90`](https://etherscan.io/address/0x000000000004444c5dc75cB358380D2e3dE08A90) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0xd1428ba554f4c8450b763a0b2040a4935c63f06c`](https://etherscan.io/address/0xd1428ba554f4c8450b763a0b2040a4935c63f06c) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e`](https://etherscan.io/address/0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x52f0e24d1c21c8a0cb1e5a5dd6198556bd9e1203`](https://etherscan.io/address/0x52f0e24d1c21c8a0cb1e5a5dd6198556bd9e1203) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x7ffe42c4a5deea5b0fec41c94c136cf115597227`](https://etherscan.io/address/0x7ffe42c4a5deea5b0fec41c94c136cf115597227) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x66a9893cc07d91d95644aedd05d03f95e1dba8af`](https://etherscan.io/address/0x66a9893cc07d91d95644aedd05d03f95e1dba8af) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Unichain: 130 | Contract | Address | | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x1f98400000000000000000000000000000000004`](https://uniscan.xyz/address/0x1f98400000000000000000000000000000000004) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x9fb28449a191cd8c03a1b7abfb0f5996ecf7f722`](https://uniscan.xyz/address/0x9fb28449a191cd8c03a1b7abfb0f5996ecf7f722) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x4529a01c7a0410167c5740c487a8de60232617bf`](https://uniscan.xyz/address/0x4529a01c7a0410167c5740c487a8de60232617bf) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x333e3c607b141b18ff6de9f258db6e77fe7491e0`](https://uniscan.xyz/address/0x333e3c607b141b18ff6de9f258db6e77fe7491e0) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x86e8631a016f9068c3f085faf484ee3f5fdee8f2`](https://uniscan.xyz/address/0x86e8631a016f9068c3f085faf484ee3f5fdee8f2) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0xef740bf23acae26f6492b10de645d6b98dc8eaf3`](https://uniscan.xyz/address/0xef740bf23acae26f6492b10de645d6b98dc8eaf3) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://uniscan.xyz/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Optimism: 10 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x9a13f98cb987694c9f086b1f5eb990eea8264ec3`](https://optimistic.etherscan.io/address/0x9a13f98cb987694c9f086b1f5eb990eea8264ec3) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0xedd81496169c46df161b8513a52ffecaaaa66743`](https://optimistic.etherscan.io/address/0xedd81496169c46df161b8513a52ffecaaaa66743) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x3c3ea4b57a46241e54610e5f022e5c45859a1017`](https://optimistic.etherscan.io/address/0x3c3ea4b57a46241e54610e5f022e5c45859a1017) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x1f3131a13296fb91c90870043742c3cdbff1a8d7`](https://optimistic.etherscan.io/address/0x1f3131a13296fb91c90870043742c3cdbff1a8d7) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0xc18a3169788f4f75a170290584eca6395c75ecdb`](https://optimistic.etherscan.io/address/0xc18a3169788f4f75a170290584eca6395c75ecdb) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x851116d9223fabed8e56c0e6b8ad0c31d98b3507`](https://optimistic.etherscan.io/address/0x851116d9223fabed8e56c0e6b8ad0c31d98b3507) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://optimistic.etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Base: 8453 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x498581ff718922c3f8e6a244956af099b2652b2b`](https://basescan.org/address/0x498581ff718922c3f8e6a244956af099b2652b2b) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x25d093633990dc94bedeed76c8f3cdaa75f3e7d5`](https://basescan.org/address/0x25d093633990dc94bedeed76c8f3cdaa75f3e7d5) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x7c5f5a4bbd8fd63184577525326123b519429bdc`](https://basescan.org/address/0x7c5f5a4bbd8fd63184577525326123b519429bdc) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x0d5e0f971ed27fbff6c2837bf31316121532048d`](https://basescan.org/address/0x0d5e0f971ed27fbff6c2837bf31316121532048d) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0xa3c0c9b65bad0b08107aa264b0f3db444b867a71`](https://basescan.org/address/0xa3c0c9b65bad0b08107aa264b0f3db444b867a71) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x6ff5693b99212da76ad316178a184ab56d299b43`](https://basescan.org/address/0x6ff5693b99212da76ad316178a184ab56d299b43) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://basescan.org/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Arbitrum One: 42161 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x360e68faccca8ca495c1b759fd9eee466db9fb32`](https://arbiscan.io/address/0x360e68faccca8ca495c1b759fd9eee466db9fb32) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0xe2023f3fa515cf070e07fd9d51c1d236e07843f4`](https://arbiscan.io/address/0xe2023f3fa515cf070e07fd9d51c1d236e07843f4) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0xd88f38f930b7952f2db2432cb002e7abbf3dd869`](https://arbiscan.io/address/0xd88f38f930b7952f2db2432cb002e7abbf3dd869) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x3972c00f7ed4885e145823eb7c655375d275a1c5`](https://arbiscan.io/address/0x3972c00f7ed4885e145823eb7c655375d275a1c5) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x76fd297e2d437cd7f76d50f01afe6160f86e9990`](https://arbiscan.io/address/0x76fd297e2d437cd7f76d50f01afe6160f86e9990) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0xa51afafe0263b40edaef0df8781ea9aa03e381a3`](https://arbiscan.io/address/0xa51afafe0263b40edaef0df8781ea9aa03e381a3) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://arbiscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Polygon: 137 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x67366782805870060151383f4bbff9dab53e5cd6`](https://polygonscan.com/address/0x67366782805870060151383f4bbff9dab53e5cd6) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x0892771f0c1b78ad6013d6e5536007e1c16e6794`](https://polygonscan.com/address/0x0892771f0c1b78ad6013d6e5536007e1c16e6794) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x1ec2ebf4f37e7363fdfe3551602425af0b3ceef9`](https://polygonscan.com/address/0x1ec2ebf4f37e7363fdfe3551602425af0b3ceef9) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0xb3d5c3dfc3a7aebff71895a7191796bffc2c81b9`](https://polygonscan.com/address/0xb3d5c3dfc3a7aebff71895a7191796bffc2c81b9) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x5ea1bd7974c8a611cbab0bdcafcb1d9cc9b3ba5a`](https://polygonscan.com/address/0x5ea1bd7974c8a611cbab0bdcafcb1d9cc9b3ba5a) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x1095692a6237d83c6a72f3f5efedb9a670c49223`](https://polygonscan.com/address/0x1095692a6237d83c6a72f3f5efedb9a670c49223) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://polygonscan.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Blast: 81457 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x1631559198a9e474033433b2958dabc135ab6446`](https://blastscan.io/address/0x1631559198a9e474033433b2958dabc135ab6446) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x0747ad2b2e1f5761b1dcf0d8672bd1ffc3676f97`](https://blastscan.io/address/0x0747ad2b2e1f5761b1dcf0d8672bd1ffc3676f97) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x4ad2f4cca2682cbb5b950d660dd458a1d3f1baad`](https://blastscan.io/address/0x4ad2f4cca2682cbb5b950d660dd458a1d3f1baad) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x6f71cdcb0d119ff72c6eb501abceb576fbf62bcf`](https://blastscan.io/address/0x6f71cdcb0d119ff72c6eb501abceb576fbf62bcf) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x12a88ae16f46dce4e8b15368008ab3380885df30`](https://blastscan.io/address/0x12a88ae16f46dce4e8b15368008ab3380885df30) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0xeabbcb3e8e415306207ef514f660a3f820025be3`](https://blastscan.io/address/0xeabbcb3e8e415306207ef514f660a3f820025be3) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://blastscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Zora: 7777777 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x0575338e4c17006ae181b47900a84404247ca30f`](https://explorer.zora.energy/address/0x0575338e4c17006ae181b47900a84404247ca30f) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x7d64630bbb4993b5578dbd65e400961c9e68d55a`](https://explorer.zora.energy/address/0x7d64630bbb4993b5578dbd65e400961c9e68d55a) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0xf66c7b99e2040f0d9b326b3b7c152e9663543d63`](https://explorer.zora.energy/address/0xf66c7b99e2040f0d9b326b3b7c152e9663543d63) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x5edaccc0660e0a2c44b06e07ce8b915e625dc2c6`](https://explorer.zora.energy/address/0x5edaccc0660e0a2c44b06e07ce8b915e625dc2c6) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x385785af07d63b50d0a0ea57c4ff89d06adf7328`](https://explorer.zora.energy/address/0x385785af07d63b50d0a0ea57c4ff89d06adf7328) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x3315ef7ca28db74abadc6c44570efdf06b04b020`](https://explorer.zora.energy/address/0x3315ef7ca28db74abadc6c44570efdf06b04b020) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://explorer.zora.energy/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Worldchain: 480 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0xb1860d529182ac3bc1f51fa2abd56662b7d13f33`](https://worldscan.org/address/0xb1860d529182ac3bc1f51fa2abd56662b7d13f33) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x7da419153bd420b689f312363756d76836aeace4`](https://worldscan.org/address/0x7da419153bd420b689f312363756d76836aeace4) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0xc585e0f504613b5fbf874f21af14c65260fb41fa`](https://worldscan.org/address/0xc585e0f504613b5fbf874f21af14c65260fb41fa) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x55d235b3ff2daf7c3ede0defc9521f1d6fe6c5c0`](https://worldscan.org/address/0x55d235b3ff2daf7c3ede0defc9521f1d6fe6c5c0) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x51d394718bc09297262e368c1a481217fdeb71eb`](https://worldscan.org/address/0x51d394718bc09297262e368c1a481217fdeb71eb) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x8ac7bee993bb44dab564ea4bc9ea67bf9eb5e743`](https://worldscan.org/address/0x8ac7bee993bb44dab564ea4bc9ea67bf9eb5e743) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://worldscan.org/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Ink: 57073 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x360e68faccca8ca495c1b759fd9eee466db9fb32`](https://explorer.inkonchain.com/address/0x360e68faccca8ca495c1b759fd9eee466db9fb32) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x42e3ccd9b7f67b5b2ee0c12074b84ccf2a8e7f36`](https://explorer.inkonchain.com/address/0x42e3ccd9b7f67b5b2ee0c12074b84ccf2a8e7f36) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x1b35d13a2e2528f192637f14b05f0dc0e7deb566`](https://explorer.inkonchain.com/address/0x1b35d13a2e2528f192637f14b05f0dc0e7deb566) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x3972c00f7ed4885e145823eb7c655375d275a1c5`](https://explorer.inkonchain.com/address/0x3972c00f7ed4885e145823eb7c655375d275a1c5) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x76fd297e2d437cd7f76d50f01afe6160f86e9990`](https://explorer.inkonchain.com/address/0x76fd297e2d437cd7f76d50f01afe6160f86e9990) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x112908dac86e20e7241b0927479ea3bf935d1fa0`](https://explorer.inkonchain.com/address/0x112908dac86e20e7241b0927479ea3bf935d1fa0) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://explorer.inkonchain.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Soneium: 1868 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x360e68faccca8ca495c1b759fd9eee466db9fb32`](https://soneium.blockscout.com/address/0x360e68faccca8ca495c1b759fd9eee466db9fb32) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x42e3ccd9b7f67b5b2ee0c12074b84ccf2a8e7f36`](https://soneium.blockscout.com/address/0x42e3ccd9b7f67b5b2ee0c12074b84ccf2a8e7f36) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x1b35d13a2e2528f192637f14b05f0dc0e7deb566`](https://soneium.blockscout.com/address/0x1b35d13a2e2528f192637f14b05f0dc0e7deb566) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x3972c00f7ed4885e145823eb7c655375d275a1c5`](https://soneium.blockscout.com/address/0x3972c00f7ed4885e145823eb7c655375d275a1c5) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x76fd297e2d437cd7f76d50f01afe6160f86e9990`](https://soneium.blockscout.com/address/0x76fd297e2d437cd7f76d50f01afe6160f86e9990) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x4cded7edf52c8aa5259a54ec6a3ce7c6d2a455df`](https://soneium.blockscout.com/address/0x4cded7edf52c8aa5259a54ec6a3ce7c6d2a455df) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://soneium.blockscout.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Avalanche: 43114 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x06380c0e0912312b5150364b9dc4542ba0dbbc85`](https://snowscan.xyz/address/0x06380c0e0912312b5150364b9dc4542ba0dbbc85) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x2b1aed9445b05ac1a3b203eccc1e25dd9351f0a9`](https://snowscan.xyz/address/0x2b1aed9445b05ac1a3b203eccc1e25dd9351f0a9) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0xb74b1f14d2754acfcbbe1a221023a5cf50ab8acd`](https://snowscan.xyz/address/0xb74b1f14d2754acfcbbe1a221023a5cf50ab8acd) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0xbe40675bb704506a3c2ccfb762dcfd1e979845c2`](https://snowscan.xyz/address/0xbe40675bb704506a3c2ccfb762dcfd1e979845c2) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0xc3c9e198c735a4b97e3e683f391ccbdd60b69286`](https://snowscan.xyz/address/0xc3c9e198c735a4b97e3e683f391ccbdd60b69286) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x94b75331ae8d42c1b61065089b7d48fe14aa73b7`](https://snowscan.xyz/address/0x94b75331ae8d42c1b61065089b7d48fe14aa73b7) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://snowscan.xyz/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### BNB Smart Chain: 56 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x28e2ea090877bf75740558f6bfb36a5ffee9e9df`](https://bscscan.com/address/0x28e2ea090877bf75740558f6bfb36a5ffee9e9df) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0xf0432f360703ec3d33931a8356a75a77d8d380e1`](https://bscscan.com/address/0xf0432f360703ec3d33931a8356a75a77d8d380e1) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x7a4a5c919ae2541aed11041a1aeee68f1287f95b`](https://bscscan.com/address/0x7a4a5c919ae2541aed11041a1aeee68f1287f95b) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x9f75dd27d6664c475b90e105573e550ff69437b0`](https://bscscan.com/address/0x9f75dd27d6664c475b90e105573e550ff69437b0) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0xd13dd3d6e93f276fafc9db9e6bb47c1180aee0c4`](https://bscscan.com/address/0xd13dd3d6e93f276fafc9db9e6bb47c1180aee0c4) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x1906c1d672b88cd1b9ac7593301ca990f94eae07`](https://bscscan.com/address/0x1906c1d672b88cd1b9ac7593301ca990f94eae07) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://bscscan.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Celo: 42220 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x288dc841A52FCA2707c6947B3A777c5E56cd87BC`](https://celoscan.io/address/0x288dc841A52FCA2707c6947B3A777c5E56cd87BC) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x5727E22b25fEEe05E8dFa83C752B86F19D102D8A`](https://celoscan.io/address/0x5727E22b25fEEe05E8dFa83C752B86F19D102D8A) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0xf7965f3981e4d5bc383bfbcb61501763e9068ca9`](https://celoscan.io/address/0xf7965f3981e4d5bc383bfbcb61501763e9068ca9) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x28566da1093609182dff2cb2a91cfd72e61d66cd`](https://celoscan.io/address/0x28566da1093609182dff2cb2a91cfd72e61d66cd) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0xbc21f8720babf4b20d195ee5c6e99c52b76f2bfb`](https://celoscan.io/address/0xbc21f8720babf4b20d195ee5c6e99c52b76f2bfb) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0xcb695bc5d3aa22cad1e6df07801b061a05a0233a`](https://celoscan.io/address/0xcb695bc5d3aa22cad1e6df07801b061a05a0233a) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://celoscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ### Monad: 143 | Contract | Address | |----------|---------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x188d586ddcf52439676ca21a244753fa19f9ea8e`](https://monadvision.com/address/0x188d586ddcf52439676ca21a244753fa19f9ea8e) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x5770d2914355a6d0a39a70aeea9bcce55df4201b`](https://monadvision.com/address/0x5770d2914355a6d0a39a70aeea9bcce55df4201b) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x5b7ec4a94ff9bedb700fb82ab09d5846972f4016`](https://monadvision.com/address/0x5b7ec4a94ff9bedb700fb82ab09d5846972f4016) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0xa222dd357a9076d1091ed6aa2e16c9742dd26891`](https://monadvision.com/address/0xa222dd357a9076d1091ed6aa2e16c9742dd26891) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x77395f3b2e73ae90843717371294fa97cc419d64`](https://monadvision.com/address/0x77395f3b2e73ae90843717371294fa97cc419d64) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x0d97dc33264bfc1c226207428a79b26757fb9dc3`](https://monadvision.com/address/0x0d97dc33264bfc1c226207428a79b26757fb9dc3) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://monadvision.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | ## Testnet Deployments ### Unichain Sepolia: 1301 | Contract | Address | |--------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x00b036b58a818b1bc34d502d3fe730db729e62ac`](https://sepolia.uniscan.xyz/address/0x00b036b58a818b1bc34d502d3fe730db729e62ac#code) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0xf70536b3bcc1bd1a972dc186a2cf84cc6da6be5d`](https://sepolia.uniscan.xyz/address/0xf70536b3bcc1bd1a972dc186a2cf84cc6da6be5d#code) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0xf969aee60879c54baaed9f3ed26147db216fd664`](https://sepolia.uniscan.xyz/address/0xf969aee60879c54baaed9f3ed26147db216fd664#code) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0xc199f1072a74d4e905aba1a84d9a45e2546b6222`](https://sepolia.uniscan.xyz/address/0xc199f1072a74d4e905aba1a84d9a45e2546b6222#code) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x56dcd40a3f2d466f48e7f48bdbe5cc9b92ae4472`](https://sepolia.uniscan.xyz/address/0x56dcd40a3f2d466f48e7f48bdbe5cc9b92ae4472#code) | | [PoolSwapTest](https://github.com/Uniswap/v4-core/blob/main/src/test/PoolSwapTest.sol) | [`0x9140a78c1a137c7ff1c151ec8231272af78a99a4`](https://sepolia.uniscan.xyz/address/0x9140a78c1a137c7ff1c151ec8231272af78a99a4#code) | | [PoolModifyLiquidityTest](https://github.com/Uniswap/v4-core/blob/main/src/test/PoolModifyLiquidityTest.sol) | [`0x5fa728c0a5cfd51bee4b060773f50554c0c8a7ab`](https://sepolia.uniscan.xyz/address/0x5fa728c0a5cfd51bee4b060773f50554c0c8a7ab#code) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://sepolia.uniscan.xyz/address/0x000000000022D473030F116dDEE9F6B43aC78BA3#code) | ### Sepolia: 11155111 | Contract | Address | |--------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0xE03A1074c86CFeDd5C142C4F04F1a1536e203543`](https://sepolia.etherscan.io/address/0xE03A1074c86CFeDd5C142C4F04F1a1536e203543#code) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x3A9D48AB9751398BbFa63ad67599Bb04e4BdF98b`](https://sepolia.etherscan.io/address/0x3A9D48AB9751398BbFa63ad67599Bb04e4BdF98b#code) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x429ba70129df741B2Ca2a85BC3A2a3328e5c09b4`](https://sepolia.etherscan.io/address/0x429ba70129df741B2Ca2a85BC3A2a3328e5c09b4#code) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0xe1dd9c3fa50edb962e442f60dfbc432e24537e4c`](https://sepolia.etherscan.io/address/0xe1dd9c3fa50edb962e442f60dfbc432e24537e4c#code) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x61b3f2011a92d183c7dbadbda940a7555ccf9227`](https://sepolia.etherscan.io/address/0x61b3f2011a92d183c7dbadbda940a7555ccf9227#code) | | [PoolSwapTest](https://github.com/Uniswap/v4-core/blob/main/src/test/PoolSwapTest.sol) | [`0x9b6b46e2c869aa39918db7f52f5557fe577b6eee`](https://sepolia.etherscan.io/address/0x9b6b46e2c869aa39918db7f52f5557fe577b6eee#code) | | [PoolModifyLiquidityTest](https://github.com/Uniswap/v4-core/blob/main/src/test/PoolModifyLiquidityTest.sol) | [`0x0c478023803a644c94c4ce1c1e7b9a087e411b0a`](https://sepolia.etherscan.io/address/0x0c478023803a644c94c4ce1c1e7b9a087e411b0a#code) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://sepolia.etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3#code) | ### Base Sepolia: 84532 | Contract | Address | |--------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x05E73354cFDd6745C338b50BcFDfA3Aa6fA03408`](https://sepolia.basescan.org/address/0x05E73354cFDd6745C338b50BcFDfA3Aa6fA03408#code) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x492e6456d9528771018deb9e87ef7750ef184104`](https://sepolia.basescan.org/address/0x492e6456d9528771018deb9e87ef7750ef184104#code) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x4b2c77d209d3405f41a037ec6c77f7f5b8e2ca80`](https://sepolia.basescan.org/address/0x4b2c77d209d3405f41a037ec6c77f7f5b8e2ca80#code) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x571291b572ed32ce6751a2cb2486ebee8defb9b4`](https://sepolia.basescan.org/address/0x571291b572ed32ce6751a2cb2486ebee8defb9b4#code) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x4a6513c898fe1b2d0e78d3b0e0a4a151589b1cba`](https://sepolia.basescan.org/address/0x4a6513c898fe1b2d0e78d3b0e0a4a151589b1cba#code) | | [PoolSwapTest](https://github.com/Uniswap/v4-core/blob/main/src/test/PoolSwapTest.sol) | [`0x8b5bcc363dde2614281ad875bad385e0a785d3b9`](https://sepolia.basescan.org/address/0x8b5bcc363dde2614281ad875bad385e0a785d3b9#code) | | [PoolModifyLiquidityTest](https://github.com/Uniswap/v4-core/blob/main/src/test/PoolModifyLiquidityTest.sol) | [`0x37429cd17cb1454c34e7f50b09725202fd533039`](https://sepolia.basescan.org/address/0x37429cd17cb1454c34e7f50b09725202fd533039#code) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://sepolia.basescan.org/address/0x000000000022D473030F116dDEE9F6B43aC78BA3#code) | ### Arbitrum Sepolia: 421614 | Contract | Address | |--------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0xFB3e0C6F74eB1a21CC1Da29aeC80D2Dfe6C9a317`](https://sepolia.arbiscan.io/address/0xFB3e0C6F74eB1a21CC1Da29aeC80D2Dfe6C9a317#code) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0xefd1d4bd4cf1e86da286bb4cb1b8bced9c10ba47`](https://sepolia.arbiscan.io/address/0xefd1d4bd4cf1e86da286bb4cb1b8bced9c10ba47#code) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0xAc631556d3d4019C95769033B5E719dD77124BAc`](https://sepolia.arbiscan.io/address/0xAc631556d3d4019C95769033B5E719dD77124BAc#code) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0x9d467fa9062b6e9b1a46e26007ad82db116c67cb`](https://sepolia.arbiscan.io/address/0x9d467fa9062b6e9b1a46e26007ad82db116c67cb#code) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x7de51022d70a725b508085468052e25e22b5c4c9`](https://sepolia.arbiscan.io/address/0x7de51022d70a725b508085468052e25e22b5c4c9#code) | | [PoolSwapTest](https://github.com/Uniswap/v4-core/blob/main/src/test/PoolSwapTest.sol) | [`0xf3a39c86dbd13c45365e57fb90fe413371f65af8`](https://sepolia.arbiscan.io/address/0xf3a39c86dbd13c45365e57fb90fe413371f65af8#code) | | [PoolModifyLiquidityTest](https://github.com/Uniswap/v4-core/blob/main/src/test/PoolModifyLiquidityTest.sol) | [`0x9a8ca723f5dccb7926d00b71dec55c2fea1f50f7`](https://sepolia.arbiscan.io/address/0x9a8ca723f5dccb7926d00b71dec55c2fea1f50f7#code) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://sepolia.arbiscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3#code) | ### interop-alpha-0: 420120000 | Contract | Address | |--------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x9131B9084E6017Be19c6a0ef23f73dbB1Bf41f96`](https://optimism-interop-alpha-0.blockscout.com/address/0x9131B9084E6017Be19c6a0ef23f73dbB1Bf41f96?tab=contract_source_code) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x4a5C956e6626c552c9e830beFDDf8F5e02bBf60a`](https://optimism-interop-alpha-0.blockscout.com/address/0x4a5C956e6626c552c9e830beFDDf8F5e02bBf60a?tab=contract_source_code) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x4498FE0b1DF6B476453440664A16E269B7587D0F`](https://optimism-interop-alpha-0.blockscout.com/address/0x4498FE0b1DF6B476453440664A16E269B7587D0F?tab=contract_source_code) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0xF3c2E547e8da2052E2fC997ee94d54FbE59a6375`](https://optimism-interop-alpha-0.blockscout.com/address/0xF3c2E547e8da2052E2fC997ee94d54FbE59a6375?tab=contract_source_code) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x7C594D9B533ac43D3595dd4117549111Ec48F8B2`](https://optimism-interop-alpha-0.blockscout.com/address/0x7C594D9B533ac43D3595dd4117549111Ec48F8B2?tab=contract_source_code) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x0ebb8526647744204cda2edb3f6bded2ec56403f`](https://optimism-interop-alpha-0.blockscout.com/address/0x0ebb8526647744204cda2edb3f6bded2ec56403f?tab=contract_source_code) | | [TransparentUpgradeableProxy](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | [`0x73162af50bf62750ff1b28c926e092b55022324d`](https://optimism-interop-alpha-0.blockscout.com/address/0x73162af50bf62750ff1b28c926e092b55022324d?tab=contract_source_code) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://optimism-interop-alpha-0.blockscout.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3?tab=contract_source_code) | ### interop-alpha-1: 420120001 | Contract | Address | |--------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`0x9131B9084E6017Be19c6a0ef23f73dbB1Bf41f96`](https://optimism-interop-alpha-1.blockscout.com/address/0x9131B9084E6017Be19c6a0ef23f73dbB1Bf41f96?tab=contract_source_code) | | [Universal Router](https://github.com/Uniswap/universal-router/blob/dev/contracts/UniversalRouter.sol) | [`0x4a5C956e6626c552c9e830beFDDf8F5e02bBf60a`](https://optimism-interop-alpha-1.blockscout.com/address/0x4a5C956e6626c552c9e830beFDDf8F5e02bBf60a?tab=contract_source_code) | | [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`0x4498FE0b1DF6B476453440664A16E269B7587D0F`](https://optimism-interop-alpha-1.blockscout.com/address/0x4498FE0b1DF6B476453440664A16E269B7587D0F?tab=contract_source_code) | | [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`0xF3c2E547e8da2052E2fC997ee94d54FbE59a6375`](https://optimism-interop-alpha-1.blockscout.com/address/0xF3c2E547e8da2052E2fC997ee94d54FbE59a6375?tab=contract_source_code) | | [Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`0x7C594D9B533ac43D3595dd4117549111Ec48F8B2`](https://optimism-interop-alpha-1.blockscout.com/address/0x7C594D9B533ac43D3595dd4117549111Ec48F8B2?tab=contract_source_code) | | [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`0x0ebb8526647744204cda2edb3f6bded2ec56403f`](https://optimism-interop-alpha-1.blockscout.com/address/0x0ebb8526647744204cda2edb3f6bded2ec56403f?tab=contract_source_code) | | [TransparentUpgradeableProxy](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | [`0x73162af50bf62750ff1b28c926e092b55022324d`](https://optimism-interop-alpha-1.blockscout.com/address/0x73162af50bf62750ff1b28c926e092b55022324d?tab=contract_source_code) | | [Permit2](https://github.com/Uniswap/permit2) | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://optimism-interop-alpha-1.blockscout.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3?tab=contract_source_code) | --- ## Calculate LP fees ## Introduction With hook introduced in v4, [Dynamic Fees](../concepts/07-dynamic-fees.mdx) is now possible and allow v4 pools to have a flexible and responsive fee structure comparing to v3 pools with static fee tiers(0.05%, 0.3%, 1%). While in v3 we can get the amount for the fees claimed on each liquidity operation in the `Collect` event, in v4 with dynamic fees, swap fees are directly accrued to liquidity positions and can be further handled by hooks at *`afterModifyLiquidity`* - thus we should not emit `feesAccrued` in the event `ModifiyLiquidity`. This not only saves gas on event emission but more importantly avoid causing confusion since the amount of `feesAccrued` could be changed as a result of hook execution. ```solidity // Modify liquidity in v4 function modifyLiquidity(PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, bytes calldata hookData) // ... returns (BalanceDelta callerDelta, BalanceDelta feesAccrued) { PoolId id = key.toId(); { // ... (other code) BalanceDelta principalDelta; (principalDelta, feesAccrued) = pool.modifyLiquidity( // ... (the ModifyLiquidityParams) ); callerDelta = principalDelta + feesAccrued; } // event is emitted before the afterModifyLiquidity call to ensure events are always emitted in order emit ModifyLiquidity(id, msg.sender, params.tickLower, params.tickUpper, params.liquidityDelta, params.salt); BalanceDelta hookDelta; (callerDelta, hookDelta) = key.hooks.afterModifyLiquidity(key, params, callerDelta, feesAccrued, hookData); // ... (other code) } ``` In this guide we will go through how you can calculate fees earned for a LP position in v4 specifically the **uncollected fees** and **total lifetime fees**. ## Contract Setup Import the necessary dependencies and prepare the function signature for `getUncollectedFees` and `getLifetimeFees`. ```solidity pragma solidity ^0.8.24; contract CalculateFeeExample { using SafeCast for uint256; using StateLibrary for IPoolManager; IPositionManager posm = IPositionManager(); IPoolManager manager = IPoolManager(); function getUncollectedFees(PositionConfig memory config, uint256 tokenId) external view returns (BalanceDelta uncollectedFees) {} function getLifetimeFees(PositionConfig memory config, uint256 tokenId) external view returns (BalanceDelta lifetimeFees) {} } ``` ### Calculate uncollected fees for a LP position In `getUncollectedFees(PositionConfig config, uint256 tokenId)`: 1. Get the last recorded fee growth in `currency0` and `currency1` per unit of liquidity from `tickLower` to `tickUpper` ```solidity PoolId poolId = config.poolKey.toId(); // getPositionInfo(poolId, owner, tL, tU, salt) // owner is the position manager, salt is the tokenId (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128) = manager.getPositionInfo(poolId, address(posm), config.tickLower, config.tickUpper, bytes32(tokenId)); ``` 2. Get the all-time fee growth in `currency0` and `currency1` per unit of liquidity from `tickLower` to `tickUpper` ```solidity (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = manager.getFeeGrowthInside(poolId, config.tickLower, config.tickUpper); ``` 3. Compare the _all-time feeGrowthInside_ from step 2 against the _last recorded feeGrowthInside_ from step 1, and convert it to token amount ```solidity uint128 tokenAmount = (FullMath.mulDiv(feeGrowthInsideX128 - feeGrowthInsideLastX128, liquidity, FixedPoint128.Q128)).toUint128(); ``` ### Calculate lifetime fees earned for a LP range Create a new function `getLifetimeFee(PositionConfig config, uint256 tokenId)`: 1. Get the all-time fee growth in `currency0` and `currency1` per unit of liquidity from `tickLower` to `tickUpper` ```solidity (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = manager.getFeeGrowthInside(poolId, config.tickLower, config.tickUpper); ``` 2. Convert it to token amount ```solidity uint128 tokenAmount = (FullMath.mulDiv(feeGrowthInsideX128, liquidity, FixedPoint128.Q128)).toUint128(); ``` ## Full Contract ```solidity pragma solidity ^0.8.24; contract CalculateFeeExample { using SafeCast for uint256; using StateLibrary for IPoolManager; IPositionManager posm = IPositionManager(); IPoolManager manager = IPoolManager(); function getUncollectedFees(PositionConfig memory config, uint256 tokenId) external view returns (BalanceDelta uncollectedFees) { PoolId poolId = config.poolKey.toId(); (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128) = manager.getPositionInfo(poolId, address(posm), config.tickLower, config.tickUpper, bytes32(tokenId)); (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = manager.getFeeGrowthInside(poolId, config.tickLower, config.tickUpper); uncollectedFees = _convertFeesToBalanceDelta( feeGrowthInside0X128 - feeGrowthInside0LastX128, feeGrowthInside1X128 - feeGrowthInside1LastX128, liquidity); } function getLifetimeFees(PositionConfig memory config, uint256 tokenId) external view returns (BalanceDelta lifetimeFees) { PoolId poolId = config.poolKey.toId(); (uint128 liquidity,,) = manager.getPositionInfo(poolId, address(posm), config.tickLower, config.tickUpper, bytes32(tokenId)); (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = manager.getFeeGrowthInside(poolId, config.tickLower, config.tickUpper); lifetimeFees = _convertFeesToBalanceDelta(feeGrowthInside0X128, feeGrowthInside1X128, liquidity); } function _convertFeesToBalanceDelta(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128, uint256 liquidity) internal view returns (BalanceDelta feesInBalanceDelta) { uint128 token0Amount = (FullMath.mulDiv(feeGrowthInside0X128, liquidity, FixedPoint128.Q128)).toUint128(); uint128 token1Amount = (FullMath.mulDiv(feeGrowthInside1X128, liquidity, FixedPoint128.Q128)).toUint128(); feesInBalanceDelta = toBalanceDelta(uint256(token0Amount).toInt128(), uint256(token1Amount).toInt128()); } } ``` --- ## Subscriber ## Introduction Uniswap v4 introduces Subscribers, a new mechanism in v4 that allows external contracts (aka Subscribers) to be notified by the PositionManager whenever specific events occur (e.g., liquidity changes, burns). It’s especially useful for designing **liquidity incentive** systems or reward programs. Before diving into implementation details, let's understand what makes Subscribers valuable. When a position holder subscribes their position to a Subscriber contract, that contract receives notifications about various events: liquidity changes, position burns, and subscription status changes. This flow enables protocols to accurately track and reward liquidity provision. Subscribers become of aware of the liquidity position, without needing to own the position and its underlying capital. ## Interface Methods Let's explore each method that a [ISubscriber](https://github.com/Uniswap/v4-periphery/blob/main/src/interfaces/ISubscriber.sol) contract needs to implement. Understanding when and why these methods are called is crucial for building effective Subscriber contracts. ### Position Subscription Handling The first two methods handle the subscription lifecycle: ```solidity function notifySubscribe( uint256 tokenId, bytes memory data ) external; ``` This method is called whenever a position subscribes to your contract. The `tokenId` parameter identifies the subscribing position, while `data` can contain additional configuration information. For example, you might use this data parameter to specify: - Lock-up duration for rewards - Campaign-specific settings - Custom reward parameters > ***Note**: The data parameter is entirely optional and its interpretation is up to your implementation.* > ```solidity function notifyUnsubscribe( uint256 tokenId ) external; ``` When a position unsubscribes, this method is called. There's an important consideration here: > ***Important**: The gas consumption of this function must be less than unsubscribeGasLimit (set when deploying the PositionManager). This ensures users can always unsubscribe from your contract. Also note that the subscriber contract reverts, the position will still unsuccessfully subscribe* > ### Position Modification Notifications The next method handles liquidity changes and fee collection: ```solidity function notifyModifyLiquidity( uint256 tokenId, int256 liquidityChange, BalanceDelta feesAccrued ) external; ``` This method is your window into position activity. It gets called in two scenarios: - When users modify their liquidity (increase or decrease) - When they collect accumulated fees Here's what each parameter tells you: - `tokenId`: The position being modified - `liquidityChange`: How much liquidity is being added or removed (positive for additions, negative for removals) - `feesAccrued`: Fees collected during this operation > ***Warning**: Be cautious with feesAccrued values. In pools with a single liquidity position, malicious users can artificially inflate these values through self-donations.* > ### Handling Position Burns The final method deals with position termination: ```solidity function notifyBurn( uint256 tokenId, address owner, PositionInfo info, uint256 liquidity, BalanceDelta feesAccrued ) external; ``` When a position is burned (fully removed), this method provides comprehensive information about its final state: - Who owned it (`owner`) - Its configuration (`info`) - How much liquidity was removed (`liquidity`) - Any final fees collected (`feesAccrued`) > ***Important**: When a position is burned, you'll only receive notifyBurn - there's no separate notifyModifyLiquidity call.* > ## How to Use Subscribers Let's walk through the process of implementing and using a Subscriber contract. We'll break this down into four main steps that developers typically follow. ### Step 1: Implement Subscriber First, create your contract implementing the ISubscriber interface: ```solidity contract MySubscriber is ISubscriber { // Track subscribed positions mapping(uint256 => bool) public isSubscribed; // Implementation of interface methods function notifySubscribe(uint256 tokenId, bytes memory data) external { // Store subscription status isSubscribed[tokenId] = true; } // Other required methods... } ``` > ***Note**: This is a basic implementation. Your actual contract will need more sophisticated tracking depending on your reward mechanism.* > ### Step 2: Deploy Subscriber Once implemented, deploy your contract: ```solidity MySubscriber subscriber = new MySubscriber(); ``` Make sure to store the deployed address - users will need it to subscribe their positions. ### Step 3: Mint Position Users need positions before they can subscribe. They can either use existing positions or mint new ones: ```solidity // Using the Position Manager uint256 tokenId = positionManager.mint( poolKey, tickLower, tickUpper, liquidity, // other parameters... ); ``` ### Step 4: Subscribe Position Finally, users can subscribe their positions to your contract: ```solidity // Optional configuration data bytes memory subscriptionData = ""; // Configure based on your needs // Subscribe the position positionManager.subscribe( tokenId, subscriber, subscriptionData ); ``` > ***Important**: Make sure users understand any lock-up periods or conditions specified in your subscriptionData.* > ### Important Considerations When implementing and deploying Subscriber contracts, there are several crucial aspects to keep in mind. Let's explore each of these to ensure your implementation is secure and efficient. ### Gas Limits and Unsubscribe Safety The most critical consideration is the unsubscribe process: > ***Critical**: Your notifyUnsubscribe function must consume less gas than the unsubscribeGasLimit defined in the PositionManager. This is not just a best practice - it's a fundamental safety requirement that ensures users can always exit your system.* > For example: ```solidity function notifyUnsubscribe(uint256 tokenId) external { // Keep this function lightweight delete positionData[tokenId]; // Simple state cleanup emit PositionUnsubscribed(tokenId); } ``` ## Implementation Patterns When building your Subscriber, follow these established patterns: ### Liquidity Tracking It's essential to properly track liquidity changes: - Use `notifyModifyLiquidity` to update your internal accounting - Remember that positive changes mean liquidity is being added - Account for negative changes when liquidity is removed > ***Note**: During a position burn, you'll only receive notifyBurn - there won't be a separate notifyModifyLiquidity call.* > ### Optional Data Handling The `optionalData` parameter in subscriptions can be used for: - Additional campaign configuration - Lock-up duration specifications - Other custom parameters ## Security Considerations Since Subscriber contracts often handle value distribution, security is paramount: ### Value Distribution - **Custody**: Implement secure mechanisms for handling reward tokens - **Calculation**: Ensure reward calculations are accurate and cannot be manipulated ### Protection Against Exploitation Be aware of potential attack vectors: - Users can artificially inflate `feesAccrued` in single-position pools - Malicious actors might attempt to claim rewards they haven't earned > ***Warning**: Always validate inputs and implement thorough checks before distributing any value.* > ## Implementation Example Let’s build a **minimal** Subscriber implementation that demonstrates how to handle position events. In practice, you’d likely extend these concepts to include reward calculations or more complex accounting. > ***Note**: The code below focuses on the essential mechanics of receiving notifications. A production system will need additional logic for security, reward distribution, and user interaction.* > ### Basic Reward Tracking First, we set up our contract with the necessary data structures and references: ```solidity contract LiquidityRewardsSubscriber is ISubscriber { // 1) Store subscription timestamps and liquidity for each position struct PositionData { uint256 subscriptionTime; uint256 currentLiquidity; bool isSubscribed; } // 2) A mapping from tokenId (position NFT) to our custom tracking data mapping(uint256 => PositionData) public positions; // 3) Reference to the PositionManager so we can authenticate notifications IPositionManager public immutable positionManager; constructor(IPositionManager _positionManager) { positionManager = _positionManager; } } ``` ### Why This Matters - **`PositionData`**: Tracks how long the position has been subscribed (`subscriptionTime`), how much liquidity it currently has (`currentLiquidity`), and whether it’s active (`isSubscribed`). - **`positionManager`**: We only trust calls from this authorized contract, preventing anyone else from triggering notifications. ## Handling Subscription Events Next, we implement `notifySubscribe` and `notifyUnsubscribe` to manage a position’s subscription status: ```solidity /// @notice Handle new position subscriptions function notifySubscribe( uint256 tokenId, bytes memory /* data */ ) external { require(msg.sender == address(positionManager), "Unauthorized"); positions[tokenId] = PositionData({ subscriptionTime: block.timestamp, currentLiquidity: 0, isSubscribed: true }); } /// @notice Clean up on unsubscribe /// @dev Keep this minimal to respect unsubscribeGasLimit function notifyUnsubscribe(uint256 tokenId) external { require(msg.sender == address(positionManager), "Unauthorized"); // Remove all stored data for unsubscribed positions delete positions[tokenId]; } ``` ### Key Takeaways - We store the current block timestamp on `notifySubscribe` to measure how long a position has been subscribed. - The `notifyUnsubscribe` method is deliberately small to ensure it remains below the `unsubscribeGasLimit`. ## Listening for Liquidity Modifications Positions can add or remove liquidity, and potentially collect fees during the same transaction: ```solidity /// @notice Track liquidity changes or fee collections function notifyModifyLiquidity( uint256 tokenId, int256 liquidityChange, BalanceDelta /* feesAccrued */ ) external { require(msg.sender == address(positionManager), "Unauthorized"); PositionData storage position = positions[tokenId]; if (liquidityChange > 0) { // Positive value indicates added liquidity position.currentLiquidity += uint256(liquidityChange); } else if (liquidityChange < 0) { // Negative value indicates removed liquidity position.currentLiquidity -= uint256(-liquidityChange); } } ``` ### Why This Is Important - **`liquidityChange`** can be positive (liquidity added) or negative (liquidity removed). We update our internal tracking accordingly. - **`feesAccrued`** is provided if the user also collects fees during this action. In a more advanced version, you could track these fees for reward calculations. ## Processing Position Burns When a position is completely removed (burned), Uniswap v4 calls `notifyBurn`: ```solidity /// @notice Called when a position is fully removed function notifyBurn( uint256 tokenId, address, PositionInfo, uint256, BalanceDelta ) external { require(msg.sender == address(positionManager), "Unauthorized"); // The position no longer exists, so remove our records delete positions[tokenId]; } ``` ### Important Distinction - **No `notifyModifyLiquidity`** is triggered on a full burn—`notifyBurn` covers any final liquidity/fee changes. ## Using the Subscriber To put this into practice: ```solidity // 1. Deploy the subscriber LiquidityRewardsSubscriber subscriber = new LiquidityRewardsSubscriber(positionManager); // 2. Users then subscribe their positions positionManager.subscribe(tokenId, subscriber, ""); ``` - **Deployment**: The developer deploying the Subscriber contract must store its address and share it with users. - **Subscription**: Users call `subscribe(...)` on the PositionManager, passing in the `tokenId` and the subscriber’s address (yours). ## Production Considerations > This example is deliberately minimal. A real-world Subscriber would need: > > - **Reward Distribution**: Logic that calculates and distributes incentives based on `subscriptionTime`, `currentLiquidity`, or `feesAccrued`. > - **Security Checks**: Safeguards against malicious actors who might inflate fees, manipulate liquidity changes, or spam subscribe/unsubscribe cycles. > - **Access Control**: Roles or permissions if only certain addresses are allowed to manage rewards or update parameters. > - **Gas Optimization**: Especially if you expect many positions to subscribe/unsubscribe frequently. --- ## ERC-6909(Guides) ## Introduction Uniswap v4 uses [ERC-6909](/contracts/v4/concepts/erc6909), a token standard that works alongside the protocol’s flash accounting system. This guide explains how ERC-6909 functions within v4, when to use mint versus burn operations, and how developers can implement them effectively. ## What is ERC-6909? ERC-6909 is a token standard that enables efficient token management within a single contract through multiple token balances per user. Where ERC-20 requires separate approve and transfer calls for token interactions, ERC-6909 provides native support for multi-token operations through mint/burn mechanics that integrate with v4’s flash accounting system. Here’s how the approaches differ: ```solidity // Traditional ERC-20 approach IERC20(tokenA).transferFrom(owner, poolManager, amount); // ERC-6909 approach in v4 poolManager.burn(owner, currency.toId(), amount); ``` ### Integration with Flash Accounting While flash accounting tracks balance changes as deltas throughout a transaction, ERC-6909 provides an additional primitive to resolve deltas. This enables: 1. Simplified transaction flows through direct mint/burn operations 2. Efficient handling of multi-step operations 3. Streamlined token management when using the PoolManager ### Gas Efficiency ERC-6909 provides gas savings compared to ERC-20 tokens, making it particularly valuable for use cases requiring frequent token transfers like: - Day trading operations - MEV bot transactions - Active liquidity management This efficiency is especially beneficial when performing multiple token operations in rapid succession. ### Simplified Token Management The traditional ERC-20 workflow requires careful management of allowances and transfers, often leading to complex transaction sequences and potential security concerns. ERC-6909 takes a different approach by providing direct balance modifications through mint and burn operations. By working through the PoolManager, all token operations are consolidated into a single interface. This means you don’t need to worry about managing multiple token approvals or keeping track of allowances across different contracts. Instead, you can focus on the core logic of your application while the PoolManager handles the token management aspects. ## Understanding ERC-6909 in v4 Let's explore how ERC-6909 is used across different v4 operations and understand when to use each of its operations. ### Operations and Token Movement Different pool operations create different types of deltas that need to be resolved: - **Swaps**: Create negative deltas for input tokens and positive deltas for output tokens - **Adding Liquidity**: Creates negative deltas (tokens you need to provide) - **Removing Liquidity**: Creates positive deltas (tokens you receive) - **Donations**: Creates negative deltas (tokens you're donating) ### Using Mint and Burn The choice between mint and burn operations depends on your token movement needs: ```solidity // When you have positive deltas (withdrawing value from PoolManager): poolManager.mint(currency, address(this), amount); // When you have negative deltas (transferring value to PoolManager): poolManager.burn(currency, address(this), amount); ``` This pattern is used throughout v4's operations: - Use mint when withdrawing value from the pool (like receiving tokens from swaps) - Use burn when transferring value to the pool (like providing tokens) ### Hook Integration When building hooks, ERC-6909 operations help manage token movements within your hook's logic: ```solidity function _beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params) internal returns (bytes4, BeforeSwapDelta, uint24) { poolManager.mint(key.currency0, address(this), amount); return ( BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0 ); } ``` Other common cases would be to use `mint` for fee collection or `burn` for token distribution. ## Implementation Let's build a contract that handles donations in v4 using ERC-6909. We'll create a donation router that follows this flow: 1. Users call our donation function with their desired amounts 2. Our contract packages this data and uses the PoolManager's unlock pattern 3. In the callback, we unpack the data and execute the donation, handling token movements using ERC-6909 First, let's set up our contract with the necessary imports and create a struct to help us pass data between functions: ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.24; contract DonationRouter { IPoolManager public immutable poolManager; // This struct helps us pack donation parameters to pass through // the unlock/callback pattern struct CallbackData { PoolKey key; uint256 amount0; uint256 amount1; bytes hookData; } constructor(IPoolManager _poolManager) { poolManager = _poolManager; } } ``` Now let's implement the external donation function. Here we'll pack our parameters into the CallbackData struct and start the unlock process: ```solidity /// @notice Donates tokens to a pool /// @param key The pool to donate to /// @param amount0 Amount of token0 to donate /// @param amount1 Amount of token1 to donate /// @param hookData Optional data to pass to hooks function donate( PoolKey memory key, uint256 amount0, uint256 amount1, bytes memory hookData ) external returns (BalanceDelta delta) { // 1. Create a CallbackData struct with all our parameters CallbackData memory data = CallbackData({ key: key, amount0: amount0, amount1: amount1, hookData: hookData }); // 2. Encode our struct into bytes to pass through unlock bytes memory encodedData = abi.encode(data); // 3. Call unlock with our encoded data // 4. unlock will call our callback, which returns encoded delta // 5. Decode the returned bytes back into a BalanceDelta delta = abi.decode( poolManager.unlock(encodedData), (BalanceDelta) ); } ``` When the PoolManager calls our callback, we need to decode our data: ```solidity function unlockCallback( bytes calldata rawData ) external returns (bytes memory) { // Only the PoolManager can trigger our callback require(msg.sender == address(poolManager)); // Decode the bytes back into our CallbackData struct // (CallbackData) tells abi.decode what type to expect CallbackData memory data = abi.decode(rawData, (CallbackData)); ``` Now `data` contains the same values we packed in donate(): - `data.key`: The pool to donate to - `data.amount0`: Amount of first token - `data.amount1`: Amount of second token - `data.hookData`: Any hook data And we can execute the donation: ```solidity // Execute the donation through PoolManager // This creates negative deltas for the tokens we're donating BalanceDelta delta = poolManager.donate( data.key, data.amount0, data.amount1, data.hookData ); ``` After executing the donation through the PoolManager, we need to handle the token transfers. The donation creates negative deltas, which represent tokens that we owe to the PoolManager. This is where ERC-6909's burn operation comes into play. Instead of using traditional token transfers, we can use ERC-6909's burn operation to settle this debt. We check each token's delta and, if negative, burn the corresponding amount of ERC-6909 tokens. And finally return the encoded delta. Let’s see how: ```solidity // Handle any negative deltas by burning ERC-6909 tokens if (delta.amount0() < 0) { poolManager.burn( data.key.currency0, address(this), uint256(-delta.amount0()) ); } if (delta.amount1() < 0) { poolManager.burn( data.key.currency1, address(this), uint256(-delta.amount1()) ); } // Encode and return the delta so donate() can decode it return abi.encode(delta); } ``` --- ## Access msg.sender Inside a Hook In Uniswap v4, when a hook is triggered, `msg.sender` is always the PoolManager contract, not the EOA or smart-account that initiated the swap. This behavior occurs because the PoolManager acts as an intermediary, executing the swap logic on behalf of the user. ## Securely Retrieving the Original `msg.sender` address in a Hook Since a smart contract executes the swap, the `sender` parameter passed to `beforeSwap()` represents the caller of `PoolManager.swap()`. This is typically a router contract, such as a custom swap router or the Universal Router. The challenge is distinguishing between different routers and securely obtaining the original msg.sender. This guide explains how to securely retrieve the EOA or smart account in a hook. ## Hook Overview To identify the true sender of a swap: * Maintain a trusted list of swap routers in the hook. * When a swap is initiated, check if the sender is a trusted router. * If trusted, call `msgSender()` view function on the router to retrieve the original EOA. # Implementing a Trusted Router Mechanism ## Implement the Hook Lets start with a simple hook that wants to access `msg.sender` in `beforeSwap()` ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; // Import statements for a hook contract AccessSenderHook is BaseHook { // constructor, state, interface, etc // ... function _beforeSwap(address sender, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata) internal override returns (bytes4, BeforeSwapDelta, uint24) { // read msg.sender // ... return (BaseHook.beforeSwap.selector, BeforeSwapDelta.wrap(0), 0); } ... } ``` > [Refer to Building your first hook](https://docs.uniswap.org/contracts/v4/guides/hooks/your-first-hook#setting-things-up) ## Define an Interface for Trusted Routers Each trusted router should implement a standard function that exposes the original ```msg.sender``` ```solidity interface IMsgSender { function msgSender() external view returns (address); } ``` This function allows hooks to query the router for the actual sender. ## Store a List of Trusted Routers The hook should keep track of which router contracts can be trusted to return a valid ```msgSender()``` This can be done with the help of add and remove functions implemented in the hook. ``` mapping(address swapRouter => bool approved) public verifiedRouters; ``` :::note Make sure you include an address mapping in your hook for the routers before adding the functions. ::: ```solidity function addRouter(address _router) external { verifiedRouters[_router] = true; console.log("Router added:", _router); } ``` This function allows hook to add the router to the list of trusted routers. ```solidity function removeRouter(address _router) external { verifiedRouters[_router] = false; console.log("Router removed:", _router); } ``` This function allows the hook to remove the router from the list of trusted routers if it's no longer needed. ## Implementing `beforeSwap` Now that we have implemented a basic hook and have added necessary functions, let us implement the beforeSwap function: ```solidity function _beforeSwap(address sender, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata) internal override returns (bytes4, BeforeSwapDelta, uint24) { try IMsgSender(sender).msgSender() returns (address swapper) { console.log("Swap initiated by account:", swapper); } catch { revert("Router does not implement msgSender()"); } return (BaseHook.beforeSwap.selector, BeforeSwapDelta.wrap(0), 0); } ``` :::note While developing, make sure that you verify the contracts are valid before adding them to the list of trusted routers. ::: > **Here are some examples of trusted routers:** * https://github.com/Uniswap/universal-router/tree/main * https://github.com/z0r0z/v4-router **Here is the full working code sample:** ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface IMsgSender { function msgSender() external view returns (address); } contract AccessSenderHook is BaseHook { constructor(IPoolManager _poolManager) BaseHook(_poolManager) { } function _beforeSwap( address sender, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata ) internal override returns (bytes4, BeforeSwapDelta, uint24) { try IMsgSender(sender).msgSender() returns (address swapper) { console.log("Swap initiated by account:", swapper); } catch { revert("Router does not implement msgSender()"); } return (BaseHook.beforeSwap.selector, BeforeSwapDelta.wrap(0), 0); } function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, beforeAddLiquidity: false, afterAddLiquidity: false, beforeRemoveLiquidity: false, afterRemoveLiquidity: false, beforeSwap: true, afterSwap: false, beforeDonate: false, afterDonate: false, beforeSwapReturnDelta: false, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } } ``` --- ## Custom Accounting ## Introduction Uniswap v4 introduces a set of powerful, interconnected features that proposes a new way automated market makers (AMMs) can function. Custom accounting, hook fees, custom curves, and return deltas might seem like distinct concepts, but they form a cohesive system that enables unprecedented flexibility in decentralized exchange mechanisms. These features are grouped together because they collectively represent the core of Uniswap v4’s customizability. They all relate to how pool state is managed and modified, working in tandem to allow developers to create highly tailored AMM experiences. From dynamic fee structures to unique bonding curves. ## Brief Overview of Concepts Before we dive into the details of custom accounting, hook fees, custom curves, and return deltas, let’s explore how these features work in Uniswap v4. ### Delta Accounting in v4 As described in [Flash Accounting](/contracts/v4/concepts/flash-accounting) Uniswap v4 tracks net token transfers with transient storage. Unlike previous versions that tracked absolute token balances, v4 records changes to these balances (*deltas*). This approach is at the core of v4’s enhanced flexibility and efficiency. In the v4 architecture, the [`PoolManager`](/contracts/v4/concepts/PoolManager) manages credits or debits per address. After a swap router contract interacts with the PoolManager, the core contract determines that the swap router owes input tokens and must claim output tokens. Token balances are tracked as accumulated deltas in transient storage; and only the final deltas incur token transfers Delta accounting provides several key benefits: 1. More efficient state management, especially for complex operations involving multiple steps. 2. Easier integration with hooks, allowing for custom logic to be applied to state changes. 3. Improved gas efficiency for many operations, as it reduces the number of storage writes. This system forms the foundation upon which other v4 features, such as hook fees and custom curves, are built. It allows for more complex pool behaviors while maintaining efficiency and flexibility. ### Hook Fees in v4 Hook fees are a feature in Uniswap v4 that allow hook developers to monetize their hooks or implement custom value distribution mechanisms. Unlike pool fees or dynamic fees, hook fees are entirely separate and are implemented through custom logic in the hook itself. Key characteristics of hook fees in Uniswap v4: **Separate from Pool Fees** Hook fees are distinct from the standard pool fees. They can be implemented alongside pool fees without interfering with the core fee structure. **Implemented in beforeSwap** Hook fees are typically calculated and applied in the `beforeSwap` function, allowing them to modify the swap parameters before the core swap logic is executed. **Use of BeforeSwapDelta** Hook fees often utilize the [`BeforeSwapDelta`](/contracts/v4/reference/core/types/beforeswapdelta) mechanism to adjust swap amounts and transfer deltas from the hook to the swap router, enabling precise control over how the fee affects the swap. **Flexible Implementation** Developers have full control over how hook fees are calculated, collected, and distributed. This allows for complex fee structures tailored to specific use cases. In other words, developers can implement static fees, percentage-based fees, or even a fee that changes. **Potential Use Cases** - Monetization of hook development - Implementation of withdrawal penalties (e.g., to penalize just-in-time liquidity provision) - Custom value distribution for liquidity providers Keep reading because at the bottom we are providing a step by step guide on how to implement hook fees. ### Custom Curves in v4 Custom Curves in Uniswap v4 represent a big change in AMM design flexibility. Unlike [Uniswap v2](/contracts/v2/concepts/protocol-overview/how-uniswap-works) where the x*y=k formula was hardcoded, v4 allows developers to implement a wide variety of pricing models. This is made possible through the hook system, particularly hooks that can interact with the swap process. Custom curves allow developers to eject the native concentrated liquidity pricing mechanism. These hooks can intercept swap requests, apply custom pricing logic, and return modified swap parameters. This enables the creation of pools with unique characteristics, such as: - Stable asset pairs with minimal price impact - Curves for special token types like rebasing tokens, RWAs, vault tokens For example, creating a custom curve for a stable swap pool would involve designing a pricing function that maintains tighter price ranges when assets are near parity. This could be achieved by implementing a curve that's flatter in the middle (where assets are at their expected 1:1 ratio) and steeper at the edges (to discourage large imbalances). This type of custom curve could significantly improve capital efficiency for stable asset pairs, reducing slippage for traders and potentially attracting more liquidity to the pool. It showcases how Uniswap v4's flexible architecture allows for tailored solutions to specific trading scenarios, opening up new possibilities in decentralized exchange design. ### Return Deltas in v4 Return deltas are a fundamental mechanism in Uniswap v4's custom accounting system. They allow for precise, programmatic adjustments to the outcomes of operations within the protocol. Key aspects of return deltas: 1. **Dual Adjustment**: Return deltas simultaneously modify the balance changes (deltas) for both the hook contract and the swap router. This dual nature ensures that custom logic is accurately reflected across the entire system. 2. **Credits and Debts Modification**: By adjusting these deltas, return deltas effectively alter the credits and debts owed by the hook and the swap router. This allows for complex economic models to be implemented directly within the protocol. 3. **Native Pricing Bypass**: Return deltas enable hooks to implement custom curves that can completely bypass Uniswap's native pricing mechanism. This opens up possibilities for entirely new types of automated market makers within the Uniswap ecosystem. 4. **Hook Fee Implementation**: Through return deltas, hooks can implement their own fee structures, separate from the core protocol fees. In essence, return deltas allow for bespoke modification of an operation's result -- enabling features that were previously impossible in earlier versions of the protocol. ## Implementing Hook Fees: A Step-by-Step Guide In this guide, we'll walk through the process of implementing a custom fee hook in Uniswap v4. We'll not only show you how to write the code but also explain what's happening under the hood at each step. ### Step 1: Setting Up the Hook First, let's create our basic hook structure: ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.26; contract HookFeeExample is BaseHook { uint256 public constant HOOK_FEE_PERCENTAGE = 10;// 0.01% fee uint256 public constant FEE_DENOMINATOR = 100000; constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, beforeAddLiquidity: false, beforeRemoveLiquidity: false, afterAddLiquidity: false, afterRemoveLiquidity: false, beforeSwap: true, afterSwap: false, beforeDonate: false, afterDonate: false, beforeSwapReturnDelta: true, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } } ``` Here, we're setting up our hook with a constant fee of 0.01% and enabling the `beforeSwap` and `beforeSwapReturnDelta` permissions. ### Step 2: Implementing the beforeSwap Function Now, let's implement our `beforeSwap` function: ```solidity function _beforeSwap( address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata ) internal override returns (bytes4, BeforeSwapDelta, uint24) { // Implementation details will be explained in the following sub-steps } ``` #### Step 2.1: Calculate the swap amount and fee We determine the absolute swap amount and calculate our fee based on it. ```solidity uint256 swapAmount = params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified); uint256 feeAmount = (swapAmount * HOOK_FEE_PERCENTAGE) / FEE_DENOMINATOR; ``` #### Step 2.2: Collect the fee We use `poolManager.take` to collect the fee. This creates a debt for our hook in the specified currency. ```solidity Currency feeCurrency; feeCurrency = params.amountSpecified < 0 == params.zeroForOne ? key.currency0 : key.currency1; poolManager.take(feeCurrency, address(this), feeAmount); ``` :::note > Using `poolManager.take()` requires an ERC20 balance on the PoolManager, i.e. via other liquidity pools. If the `.take()` amount exceeds the ERC20 balance, the code will revert. As a workaround, use either: >1. `poolManager.mint()` to obtain ERC6909, which are also more gas efficient >2. A custom swap router, where input tokens are transferred to PoolManager before the router calls `poolManager.swap()` ::: #### Step 2.3: Create the BeforeSwapDelta This is where the magic happens. We create a `BeforeSwapDelta` that represents our fee. The `toBeforeSwapDelta` function takes two parameters: - The specified delta: This is our fee amount. It's positive because we're adding it to the hook's balance. - The unspecified delta: We set this to 0 as we're not affecting the other currency. ```solidity BeforeSwapDelta returnDelta = toBeforeSwapDelta( int128(int256(feeAmount)), // Specified delta (fee amount) 0 // Unspecified delta (no change) ); ``` #### Step 2.4: Return values We return the function selector, our `returnDelta`, and 0 for the fee override. ```solidity return (BaseHook.beforeSwap.selector, returnDelta, 0); ``` ### Step 3: Understanding the BeforeSwapDelta Mechanism Now, let's dive deeper into how the `BeforeSwapDelta` works and how it affects the overall swap process. 1. **Initial State**: Let's say a user wants to swap 100 USDC for USDT - Hook's delta: (0, 0) - User's swap request: -100 USDC (negative because they're selling) 2. **After Hook Execution**: Our hook has taken a 1 USDC fee (assuming 1% for simplicity): - Hook's delta: (-1 USDC, 0) // The hook now owes 1 USDC to the pool - BeforeSwapDelta returned: (1 USDC, 0) // This will be added to the hook's delta and subtracted from the swap delta 3. **PoolManager Processing**: The PoolManager applies our `BeforeSwapDelta` The pool then swaps 99 USDC for, let's say, 98 USDT. ```solidity amountToSwap = params.amountSpecified + hookDelta.getSpecifiedDelta(); -99 USDC = -100 USDC + 1 USDC ``` 4. **Delta Resolution**: The PoolManager then resolves the deltas: ```solidity // Hook's new delta newHookDelta = oldHookDelta + returnDelta (0, 0) = (-1 USDC, 0) + (1 USDC, 0) // Swap delta for router swapDelta = (-99 USDC, 98 USDT) - (1 USDC, 0) = (-100 USDC, 98 USDT) ``` 5. **Final Outcome**: - The hook's debt is cleared: It took 1 USDC as a fee, but "returned" it to the swap process. - The router (on behalf of the user) must pay 100 USDC (original amount including fee) and receives 98 USDT. This process demonstrates how `BeforeSwapDelta` effectively "transfers" the hook's outstanding delta to the swap router, ensuring that the user pays the fee while the hook collects it, all within a single atomic transaction. ## Alternative: Charging Hook Fees in afterSwap Charging hook fees in `afterSwap` is different from charging them in `beforeSwap` because `afterSwap` allows the hook to compute a fee on the actual output and collect it. This is best used in case of partial swaps, as `afterSwap` charges a fee on the **actual output** observed from `BalanceDelta` without overcharging the user. ### When to prefer afterSwap? - You want fees to reflect **what actually transferred** (not the requested amount). - Your swaps frequently **hit price limits** or **liquidity ceilings**. ### Step 1: Setting the permission flags Enable `afterSwap` and `afterSwapReturnDelta` permissions in the hook: ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.26; contract HookFeeAfterSwap is BaseHook { uint256 public constant HOOK_FEE_PERCENTAGE = 10; uint256 public constant FEE_DENOMINATOR = 100_000; constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, beforeAddLiquidity: false, beforeRemoveLiquidity: false, afterAddLiquidity: false, afterRemoveLiquidity: false, beforeSwap: false, afterSwap: true, beforeDonate: false, afterDonate: false, beforeSwapReturnDelta: false, afterSwapReturnDelta: true, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } } ``` ### Step 2: Implementing `afterSwap` function: In afterSwap, you do not build a BeforeSwapDelta. Instead, we read actual output from **BalanceDelta** and compute fee on that amount. :::tip Unspecified currency (afterSwap) The `int128` you return is for the **unspecified currency**. Map it like this: - **exactIn** → unspecified = **output** token → take the fee in **output**; user receives less output. - **exactOut** → unspecified = **input** token → take the fee in **input**; user must pay more input. ::: ```solidity function _afterSwap( address, PoolKey calldata key, SwapParams calldata params, BalanceDelta delta, bytes calldata ) internal override returns (bytes4, int128) { bool outputIsToken0 = params.zeroForOne ? false : true; int256 outputAmount = outputIsToken0 ? delta.amount0() : delta.amount1(); if (outputAmount <= 0) { return (BaseHook.afterSwap.selector, 0); } uint256 feeAmount = (uint256(outputAmount) * HOOK_FEE_PERCENTAGE) / FEE_DENOMINATOR; require(feeAmount <= ((uint256(1) << 127) - 1), "fee too large"); bool isExactIn = (params.amountSpecified < 0); Currency feeCurrency; if (isExactIn) { feeCurrency = outputIsToken0 ? key.currency0 : key.currency1; } else { bool inputIsToken0 = params.zeroForOne ? true : false; feeCurrency = inputIsToken0 ? key.currency0 : key.currency1; } poolManager.take(feeCurrency, address(this), feeAmount); return (BaseHook.afterSwap.selector, int128(int256(feeAmount))); ``` ### Complete afterSwap contract: ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.26; contract HookFeeAfterSwap is BaseHook { uint256 public constant HOOK_FEE_PERCENTAGE = 10; uint256 public constant FEE_DENOMINATOR = 100_000; constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, beforeAddLiquidity: false, beforeRemoveLiquidity: false, afterAddLiquidity: false, afterRemoveLiquidity: false, beforeSwap: false, afterSwap: true, beforeDonate: false, afterDonate: false, beforeSwapReturnDelta: false, afterSwapReturnDelta: true, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } function _afterSwap( address , PoolKey calldata key, SwapParams calldata params, BalanceDelta delta, bytes calldata ) internal override returns (bytes4, int128) { bool outputIsToken0 = params.zeroForOne ? false : true; int256 outputAmount = outputIsToken0 ? delta.amount0() : delta.amount1(); if (outputAmount <= 0) { return (BaseHook.afterSwap.selector, 0); } uint256 feeAmount = (uint256(outputAmount) * HOOK_FEE_PERCENTAGE) / FEE_DENOMINATOR; require(feeAmount <= ((uint256(1) << 127) - 1), "fee too large"); bool isExactIn = (params.amountSpecified < 0); Currency feeCurrency; if (isExactIn) { feeCurrency = outputIsToken0 ? key.currency0 : key.currency1; } else { bool inputIsToken0 = params.zeroForOne ? true : false; feeCurrency = inputIsToken0 ? key.currency0 : key.currency1; } poolManager.take(feeCurrency, address(this), feeAmount); return (BaseHook.afterSwap.selector, int128(int256(feeAmount))); } } ``` ## Conclusion By implementing hook fees this way, we've leveraged Uniswap v4's delta accounting system to create a seamless fee collection mechanism. This approach allows for complex fee structures and behaviors without disrupting the core swap process or requiring separate fee transfers. --- ## Flash Accounting(Guides) ## Introduction Flash accounting is v4’s mechanism for tracking token movements throughout a transaction. Unlike traditional token accounting which updates balances immediately after each operation, flash accounting accumulates changes (deltas) and settles them at the end of the transaction. ## How Flash Accounting Works When interacting with v4's PoolManager, all token movements follow a consistent pattern: negative values represent tokens moving from users to the PoolManager, while positive values represent tokens moving from the PoolManager to users. This pattern appears in operations like swaps and liquidity management, where: - Negative values indicate tokens going to the PoolManager - Positive values indicate tokens coming from the PoolManager These movements are tracked through deltas that represent token obligations: - Negative deltas indicate tokens owed to the PoolManager - Positive deltas indicate tokens the PoolManager owes to an address ## The PoolManager Lock Pattern All operations that access pool liquidity must occur while the PoolManager is unlocked. This pattern ensures atomic execution and proper delta tracking: 1. Unlock the PoolManager 2. Execute operations (creating deltas) 3. Resolve all deltas 4. Context returns to the PoolManager which verifies no outstanding deltas If any deltas remain unresolved when the PoolManager locks, the entire transaction reverts. This guarantees that all token movements balance out by the end of the transaction. ## Understanding the Basics Before diving into implementation patterns, let’s look at the key concepts you’ll need to work with flash accounting. Each example includes common scenarios you’ll encounter when building on v4. ### Working with Deltas Every operation in v4 that involves tokens creates deltas. These deltas track what the executor owes to the PoolManager and vice versa: ```solidity // Example: Executing a swap // Note: This assumes the PoolManager has been unlocked function executeSwap(PoolKey calldata key) external { // A swap returns a BalanceDelta BalanceDelta delta = poolManager.swap( key, IPoolManager.SwapParams({ zeroForOne: true, amountSpecified: -1e18, // Negative means spending/providing 1 ETH sqrtPriceLimitX96: MAX_SQRT_RATIO - 1 // Max price willing to accept }), "" ); // Delta shows: // delta.amount0() = -1e18 (executor owes 1 ETH) // delta.amount1() = +2000e6 (executor receives 2000 USDC) } ``` When a swap is executed, the PoolManager returns a `BalanceDelta` that shows your token obligations. In this example, the negative delta (-1e18) means the executor owes 1 ETH to the PoolManager, while the positive delta (+2000e6) means the executor is entitled to receive 2000 USDC. These deltas must be resolved before the transaction completes. _Note how negative values in v4 consistently represent tokens going to the PoolManager - both in `amountSpecified` for the input amount and in the returned delta for tokens owed._ ### Reading Delta States A common pattern is checking current deltas before executing operations. The `TransientStateLibrary` helps you track these balances: ```solidity contract DeltaReader { using TransientStateLibrary for IPoolManager; function checkDeltaBeforeOperation( Currency currency, address user ) external view returns (int256) { // Important: This shows the current delta for this token/user pair return poolManager.currencyDelta(user, currency); // Negative: User owes tokens // Positive: User can claim tokens // Zero: No outstanding obligations } } ``` The `TransientStateLibrary` provides utilities to check the current state of deltas at any point in your transaction. The `currencyDelta` function returns an int256 where negative values indicate the user owes tokens to the PoolManager, positive values mean the user can claim tokens from the PoolManager, and zero means there are no outstanding obligations for this token/user pair. ### Resolving Deltas You must resolve all deltas before your transaction completes. There are two main approaches: **1. Using ERC-20 Functions** When using ERC-20 tokens, settling requires a specific sequence of operations: ```solidity function resolveWithERC20( Currency currency, uint256 amount ) external { // For negative deltas (you owe tokens): if (!currency.isAddressZero()) { // If not ETH poolManager.sync(currency); // Sync currency balance first IERC20Minimal(Currency.unwrap(currency)).transfer( address(poolManager), amount ); poolManager.settle(); // Complete the settlement } // For positive deltas (receiving tokens): poolManager.take(currency, address(this), amount); } ``` When resolving negative deltas with ERC-20 tokens, you need to: 1. Sync the currency balance with `sync()` 2. Transfer the tokens to the PoolManager 3. Complete the settlement with `settle()` For positive deltas, simply use `take` to receive tokens from the PoolManager. **2. Using ERC-6909 Functions** ```solidity function resolveWithERC6909( Currency currency, uint256 amount ) external { // For negative deltas (you owe tokens): poolManager.burn(currency, address(this), amount); // For positive deltas (receiving tokens): poolManager.mint(currency, address(this), amount); } ``` ERC-6909 operations map to their ERC-20 equivalents in v4: - Use `burn` when you would use `settle` (for negative deltas) - Use `mint` when you would use `take` (for positive deltas) Notice how this pattern requires no additional sync operations or separate token transfers. > **Important**: *Every delta must be resolved before the transaction ends, or the entire transaction will revert. Use* `TransientStateLibrary` *to verify your balances are properly settled.* > > _Delta is a net balance resulting from token movements thus not bound to a certain token type i.e. can be resolved via mix-and-match with ERC-20 functions and ERC-6909 functions._ ## Working with Flash Accounting To interact with the PoolManager, we first need to create the functions our users will call. Then we'll implement the unlock callback pattern required to execute these operations. ### Using the Lock/Unlock Pattern Let's start by creating our external function. First, we need to implement the callback that the `PoolManager` will use: ```solidity function unlockCallback(bytes calldata data) external returns (bytes memory) { // To be implemented later } ``` Now let's implement our external function that users will call: ```solidity function executeSwap( PoolKey calldata key, uint256 amount ) external returns (int256, int256) { // Encode operation parameters bytes memory data = abi.encode(key, amount); // Call unlock with encoded data bytes memory result = poolManager.unlock(data); // Optional: Decode any relevant return data return (0, 0); // Replace with actual return values if needed } ``` When you call this function the flow followed is the following: 1. `unlock` is called on the PoolManager 2. PoolManager calls back to your `unlockCallback` 3. Your callback executes the operations 4. All deltas must be resolved before returning 5. Execution of the logic returns to the PoolManager which verifies there are no outstanding deltas, and will relock itself > **Warning***: Always implement proper access control in your unlock callback. Only the PoolManager should be able to call it.* > ### Implementing the Unlock Callback First, let’s set up a contract with the proper unlock callback implementation: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract FlashAccountingExample { IPoolManager public immutable poolManager; constructor(IPoolManager _poolManager) { poolManager = _poolManager; } function executeSwap( PoolKey calldata key, uint256 amount ) external returns (int256, int256) { ... } function unlockCallback(bytes calldata data) external returns (bytes memory) { // Important: Must check caller is PoolManager require(msg.sender == address(poolManager), "Not pool manager"); // Decode and call our executeOperations function which // we'll implement next (bytes memory result) = executeOperations(data); // Important: Must return bytes, even if empty return result; } } ``` This base contract sets up the foundation for working with v4’s flash accounting. The `unlockCallback` function is required for any operations that access pool liquidity - when your contract calls `poolManager.unlock()`, the PoolManager calls back to this function to execute your operations. The callback must verify it's being called by the PoolManager and return a bytes value (even if empty) to prevent transaction failures. Any actual pool operations (like swaps or liquidity changes) will be handled through the `executeOperations` function. > **Critical Note***: The most common mistake developers make is not returning a bytes value from unlockCallback. This will cause your transaction to revert. Always return a bytes value, even if it’s empty.* > Let’s add functionality to execute operations: ```solidity function executeOperations( bytes calldata data ) internal returns (bytes memory) { // Decode operation parameters (PoolKey memory key, uint256 amount) = abi.decode( data, (PoolKey, uint256) ); // Execute operation (e.g. swap) BalanceDelta delta = poolManager.swap( key, IPoolManager.SwapParams({ zeroForOne: true, amountSpecified: -int256(amount), sqrtPriceLimitX96: 0 }), "" ); // Resolve deltas if (delta.amount0() < 0) { poolManager.sync(key.currency0); IERC20Minimal(Currency.unwrap(key.currency0)).transfer( address(poolManager), uint256(-delta.amount0()) ); poolManager.settle(); } if (delta.amount1() > 0) { poolManager.take( key.currency1, address(this), uint256(delta.amount1()) ); } return ""; // Return empty bytes if no specific result needed } ``` The `executeOperations` function handles the actual pool operations. It first decodes the data passed from the unlock call to get the operation parameters. In this example, it executes a swap which creates deltas (token obligations) that must be resolved. For negative deltas (tokens we owe), we follow a specific sequence: first sync the currency state, then transfer the tokens to the PoolManager, and finally call settle. For positive deltas (tokens we receive), we use take to claim them. All deltas must be resolved before the function returns or the transaction will revert. ## Managing Liquidity with Flash Accounting When adding or removing liquidity in v4, you’ll use `modifyLiquidity` which creates deltas that need to be handled through flash accounting. Let's understand how this works. ### Adding Liquidity ```solidity // Example: Adding liquidity creates negative deltas (you need to provide tokens) BalanceDelta delta = poolManager.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: tickLower, // Lower price bound for position tickUpper: tickUpper, // Upper price bound for position liquidityDelta: liquidityAmount // Positive for adding liquidity }), "" // No hook data needed ); // Negative deltas for both tokens // delta.amount0() = -100 (need to provide token0) // delta.amount1() = -200 (need to provide token1) ``` When adding liquidity to a pool, you’ll need to provide both tokens in the pair. The `modifyLiquidity` function returns a [`BalanceDelta`](/contracts/v4/reference/core/types/balancedelta) that indicates how many tokens you need to provide. In this case: - The negative values in the delta (-100, -200) indicate you need to provide these amounts of each token - The values are proportional to the current pool price and your specified price range (tickLower to tickUpper) - These deltas must be resolved by providing the tokens before the transaction completes ### Removing Liquidity ```solidity // Example: Removing liquidity creates positive deltas (you receive tokens) BalanceDelta delta = poolManager.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: tickLower, // Same position bounds as when added tickUpper: tickUpper, liquidityDelta: -liquidityAmount // Negative for removing liquidity }), "" // No hook data needed ); // Positive deltas for both tokens // delta.amount0() = +100 (receive token0) // delta.amount1() = +200 (receive token1) ``` When removing liquidity, the process is reversed. The negative `liquidityDelta` indicates you're removing liquidity, and the function returns positive deltas representing the tokens you'll receive: - The positive values (+100, +200) indicate the amounts you’ll receive of each token - The amounts depend on the pool’s current state and how much liquidity you’re removing - These positive deltas represent tokens you can claim from the pool > **Important***: Unlike single token operations, liquidity management typically involves handling deltas for both tokens in the pool.* > --- ## Hook Deployment ## Hook Flags As mentioned in [Concept of Hooks](../../concepts/hooks.mdx), hook contracts indicate their implemented functions by __encoding its behavior in the address of the contract__. The `PoolManager` uses these permissions to determine which hook functions to call for a given pool. See `PoolManager` deployment addresses [here](/contracts/v4/deployments). Each hook function e.g. `beforeSwap` - corresponds to a certain _flag_. For example, the `beforeSwap` function is correlated to the [`BEFORE_SWAP_FLAG`](https://github.com/Uniswap/v4-core/blob/main/src/libraries/Hooks.sol#L37) which has a value of `1 << 7`. These flags represent specific bits in the address of the hook smart contract - and the value of the bit (a one or a zero) represents whether that flag is true or false. An example: Addresses on Ethereum are 20 bytes long (160 bits). So for example the address: ``` 0x00000000000000000000000000000000000000C0 ``` represented in binary is: ```solidity 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1100 0000 ``` In binary it goes from right-to-left - so the trailing 8 bits of this address are `1100 0000` where: 1st Bit to 6th Bit = `0` 7th Bit and 8th Bit = `1` The `AFTER_SWAP` flag is represented by the 7th bit - which is set to `1` for the example contract address. In the `PoolManager` swap execution flow, it will observe the flag and make a call to the hook's `afterSwap` function. Similarly, the 8th bit which is also a `1`, actually corresponds to the `BEFORE_SWAP` i.e. the `beforeSwap` hook function - which will also be called by the `PoolManager` during a `swap` workflow. A full list of all flags can be found [here](https://github.com/Uniswap/v4-core/blob/main/src/libraries/Hooks.sol). ## Hook Miner Because of encoded addresses, hook developers must _mine_ an address to a their particular pattern. For local testing, `deployCodeTo` cheatcode in Foundry can be used to deploy hook contract to any address. But when deploying hooks to an actual network, address mining is required to find the proper deployment address There is a helper library [`HookMiner.sol`](https://github.com/Uniswap/v4-periphery/blob/main/src/utils/HookMiner.sol) that can be used to mine for correct addresses. Let's see it in action for a Foundry script. We will make use of the example - Points Hook from [Building Your First Hook](./01-your-first-hook.md). First we set up the contract for Foundry script and import the relevant dependencies: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /// @notice Mines the address and deploys the PointsHook.sol Hook contract contract PointsHookScript is Script, Constants { function setUp() public {} function run() public { ``` Specify the flags needed to be encoded in the address: ```solidity uint160 flags = uint160( Hooks.AFTER_ADD_LIQUIDITY_FLAG | Hooks.AFTER_SWAP_FLAG ); ``` Mine the address by finding a `salt` that produces a hook address with the desired `flags`, use the Foundry deterministic deployer when deploying via Foundry script. For most chains, CREATE2_DEPLOYER contract address is [0x4e59b44847b379578588920ca78fbf26c0b4956c](https://book.getfoundry.sh/guides/deterministic-deployments-using-create2#getting-started). ```solidity bytes memory constructorArgs = abi.encode(POOLMANAGER); (address hookAddress, bytes32 salt) = HookMiner.find(CREATE2_DEPLOYER, flags, type(PointsHook).creationCode, constructorArgs); ``` :::note **CREATE2_DEPLOYER** - `CREATE2_DEPLOYER` is the address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address. - In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) ::: Refer to this for more details on deploying contracts with CREATE2: [Deploying Contracts with CREATE2](https://docs.openzeppelin.com/cli/2.8/deploying-with-create2) Deploy the hook using CREATE2 with the `salt`, and compare the deployed address with the address mined: ```solidity vm.broadcast(); PointsHook pointsHook = new PointsHook{salt: salt}(IPoolManager(POOLMANAGER)); require(address(pointsHook) == hookAddress, "PointsHookScript: hook address mismatch"); ``` ## A Complete Foundry Script Contract ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /// @notice Mines the address and deploys the PointsHook.sol Hook contract contract PointsHookScript is Script, Constants { 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 bytes memory constructorArgs = abi.encode(POOLMANAGER); (address hookAddress, bytes32 salt) = HookMiner.find(CREATE2_DEPLOYER, flags, type(PointsHook).creationCode, constructorArgs); // Deploy the hook using CREATE2 vm.broadcast(); PointsHook pointsHook = new PointsHook{salt: salt}(IPoolManager(POOLMANAGER)); require(address(pointsHook) == hookAddress, "PointsHookScript: hook address mismatch"); } } ``` --- ## Building Your First Hook ## Introduction Uniswap introduced the v4 of their protocol introducing several new concepts such as hooks, flash accounting, singleton architecture and more. The most interesting of these for developers is hooks, and that’s what we’ll be learning about today. In this guide, we’ll be conceptualizing, understanding and building a basic points hook, which will give you some idea of how to build your own hook. ## What are we building? Let’s start by conceptualizing what we’re building today and why. Let’s say you have a token named `TOKEN` that you want to incentivize people to buy. One way of doing so is awarding people points when they buy your token. Prior to v4, you’d have to do this off-chain or via your own helper contract outside of the swap logic, but in v4 you can enable universal access using hooks. Let’s start by defining when users will be rewarded with these points: 1. When the user swaps `ETH` into `TOKEN` they will get awarded points equal to how much `ETH` they swapped the token with. 2. When the user adds liquidity, we award them with points equal to the amount of `ETH` they added. In order to keep track of these points, we’ll mint the `POINTS` token to the user, this has an added benefit for the user to be able to track it in their wallet. ## Hook Design Let’s figure out how our hook will work. From the Uniswap v4 Documentation, there are several hooks available for developers to integrate with. In our use case, we specifically need the ability to read swaps and figure out what amounts they are swapping for and who they are. For our hook, we’ll be using `afterSwap` and `afterAddLiquidity` hooks. Why these instead of the `before...` hooks? We’ll dig deeper into this later in this guide. _Note: To initiate the swap in the first place, this is where [`UniversalRouter`](../../../../contracts/universal-router/01-overview.md) comes into play where we will pass in the [`V4_SWAP`](https://github.com/Uniswap/universal-router/blob/main/contracts/libraries/Commands.sol#L35) command to `UniversalRouter.execute`._ ## Let’s create our hook! We’ll call this hook `PointsHook` and create it in such a way that any pool paired with `TOKEN` can use it. ### Setting things up The Uniswap [v4-template repo](https://github.com/uniswapfoundation/v4-template) provides a basic foundry environment with required imports already pre-loaded. Click on [`Use this template`](https://github.com/new?template_name=v4-template&template_owner=uniswapfoundation) to create a new repository with it. Or simply clone it and install the dependencies: ```bash git clone https://github.com/uniswapfoundation/v4-template.git cd v4-template # requires foundry forge install forge test ``` After that let's create a new contract `PointsHook.sol` in `src` folder with the following codes: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract PointsHook is BaseHook { constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, beforeAddLiquidity: false, afterAddLiquidity: true, beforeRemoveLiquidity: false, afterRemoveLiquidity: false, beforeSwap: false, afterSwap: true, beforeDonate: false, afterDonate: false, beforeSwapReturnDelta: false, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } } ``` The above code does the following: - import the relevant dependencies - initialize the constructor by passing in the instance of PoolManager - override `getHookPermissions` from `BaseHook.sol` to return a struct of permissions to signal which hook functions are to be implemented. It will also be used at deployment to validate the address correctly represents the expected permissions. Awesome! Now it's all set to start building the hook! ### Basic Structure So far, we’ve created the file named `PointsHook.sol` which only contains the outline of a hook contract. Let’s add our `afterSwap` and `afterAddLiquidity` hooks to it. ```solidity contract PointsHook is BaseHook { constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, beforeAddLiquidity: false, afterAddLiquidity: true, beforeRemoveLiquidity: false, afterRemoveLiquidity: false, beforeSwap: false, afterSwap: true, beforeDonate: false, afterDonate: false, beforeSwapReturnDelta: false, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } function _afterSwap( address, PoolKey calldata key, IPoolManager.SwapParams calldata, BalanceDelta delta, bytes calldata ) internal override returns (bytes4, int128) { return (BaseHook.afterSwap.selector, 0); } function _afterAddLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, BalanceDelta delta, BalanceDelta feesAccrued, bytes calldata hookData ) internal override returns (bytes4, BalanceDelta) { return (BaseHook.afterAddLiquidity.selector, delta); } } ``` You’ll notice that both hooks return their own selector in the functions, this is a pattern used by the protocol to signal “successful” invocation. We’ll talk about rest of the return parameters when we start adding the functionality. Most of the code at this point should be self-explanatory. It’s not doing anything yet, but it’s a great place to start adding the functionality we need. ### Points Logic First, let’s setup the `POINTS` token that we’ll reward users with via creating another contract `PointsToken.sol` and import relevant dependencies like `ERC20` and `Owned`. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract PointsToken is ERC20, Owned { constructor() ERC20("Points Token", "POINTS", 18) Owned(msg.sender) {} function mint(address to, uint256 amount) external onlyOwner { _mint(to, amount); } } ``` Let’s make it so that our hook can mint some! ```solidity contract PointsHook is BaseHook { PointsToken public pointsToken; constructor(IPoolManager _poolManager) BaseHook(_poolManager) { pointsToken = new PointsToken(); } [...] } ``` Next, we need to calculate how many points to assign based on the `ETH` value of the swap or liquidity action. We’ll be awarding `POINTS` in 1:1 ratio for the `ETH`, so if the user swapped 1 `ETH`, we’ll give them 1 `POINTS`. Let’s also create a function to award these to the user. ```solidity function getPointsForAmount( uint256 amount ) internal pure returns (uint256) { return amount; // 1:1 with ETH } function awardPoints(address to, uint256 amount) internal { pointsToken.mint(to, getPointsForAmount(amount)); } ``` ### Hook Logic Now we need to actually get the value that the user is swapping or adding liquidity with. We’ll be using the two hooks to achieve that functionality. #### Getting the user address Before we go into the logic for the hook, we have a side quest! How do we actually get the address of the user? The `PositionManager` doesn’t pass the user address directly to the hook, mainly because of the complexity of getting that data in the first place. You’d have noticed, both of our hooks have a `hookData` field at the end. This allows any arbitrary data to be passed to the hook at the time of invocation, and we’ll use this field to encode the user address. Let’s create some helper functions to encode and decode this data: ```solidity function getHookData(address user) public pure returns (bytes memory) { return abi.encode(user); } function parseHookData( bytes calldata data ) public pure returns (address user) { return abi.decode(data, (address)); } ``` #### Hook Logic: `afterSwap` In order for us to award these points to the user, we need a few things and we also need to create a few conditions. Let’s start with the most basic ones. We want the user to be swapping in the `ETH/TOKEN` pool and be buying the `TOKEN` in order to get awarded these `POINTS` token. Next, we need to figure out who the user is and how much ETH they are spending, and finally award the points accordingly. ```solidity function _afterSwap( address, PoolKey calldata key, IPoolManager.SwapParams calldata swapParams, BalanceDelta delta, bytes calldata hookData ) internal override onlyPoolManager returns (bytes4, int128) { // We only award points in the ETH/TOKEN pools. if (!key.currency0.isAddressZero()) { return (BaseHook.afterSwap.selector, 0); } // We only award points if the user is buying the TOKEN if (!swapParams.zeroForOne) { return (BaseHook.afterSwap.selector, 0); } // Let's figure out who's the user address user = parseHookData(hookData); // How much ETH are they spending? uint256 ethSpendAmount = uint256(int256(-delta.amount0())); // And award the points! awardPoints(user, ethSpendAmount); return (BaseHook.afterSwap.selector, 0); } ``` That middle section about figuring out how much `ETH` the user spent seems a little fishy, what’s going on there? Let’s break it down. When `amountSpecified` is less than 0, it means this is an `exact input for output` swap, essentially where the user is coming in with an exact amount of ETH. In the other case, it’s an `exact output for input` swap, where the user is expecting a specific amount out. In our case, we get this from the precalculated `delta` that Uniswap V4 gives us as a part of the `afterSwap` hook. #### Hook Logic: `afterAddLiquidity` Similar to what we did for the `afterSwap` hook, now we need to award users for adding liquidity. We’ll do the exact same thing here, except we’ll award the points based on the added liquidity. ```solidity function _afterAddLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, BalanceDelta delta, BalanceDelta feesAccrued, bytes calldata hookData ) internal override onlyPoolManager returns (bytes4, BalanceDelta) { // We only award points in the ETH/TOKEN pools. if (!key.currency0.isAddressZero()) { return (BaseHook.afterAddLiquidity.selector, delta); } // Let's figure out who's the user address user = parseHookData(hookData); // How much ETH are they spending? uint256 ethSpendAmount = uint256(int256(-delta.amount0())); // And award the points! awardPoints(user, ethSpendAmount); return (BaseHook.afterAddLiquidity.selector, delta); } ``` :::note It is important to note that the delta should be passed to awardPoints function as it avoids amount errors in case of partial fills. ::: ## Testing We’re using Foundry for building our hook, and we’ll continue using it to write our tests. One of the great things about Foundry is that you can write tests in Solidity itself instead of context switching between another language. ### Test Suite The v4-template repo you cloned already has an existing base test file, let’s start by copying it into `PointsHook.t.sol`. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract PointsHookTest is Test, Fixtures { using EasyPosm for IPositionManager; using StateLibrary for IPoolManager; PointsHook hook; PointsToken pointsToken; PoolId poolId; uint256 tokenId; int24 tickLower; int24 tickUpper; function setUp() public { // creates the pool manager, utility routers, and test tokens deployFreshManagerAndRouters(); deployMintAndApprove2Currencies(); deployAndApprovePosm(manager); // Deploy the hook to an address with the correct flags address flags = address( uint160(Hooks.AFTER_SWAP_FLAG | Hooks.AFTER_ADD_LIQUIDITY_FLAG) ^ (0x4444 << 144) // Namespace the hook to avoid collisions ); bytes memory constructorArgs = abi.encode(manager); //Add all the necessary constructor arguments from the hook deployCodeTo("PointsHook.sol:PointsHook", constructorArgs, flags); hook = PointsHook(flags); pointsToken = hook.pointsToken(); // Create the pool key = PoolKey( Currency.wrap(address(0)), currency1, 3000, 60, IHooks(hook) ); poolId = key.toId(); manager.initialize(key, SQRT_PRICE_1_1); // Provide full-range liquidity to the pool tickLower = TickMath.minUsableTick(key.tickSpacing); tickUpper = TickMath.maxUsableTick(key.tickSpacing); deal(address(this), 200 ether); (uint256 amount0, uint256 amount1) = LiquidityAmounts .getAmountsForLiquidity( SQRT_PRICE_1_1, TickMath.getSqrtPriceAtTick(tickLower), TickMath.getSqrtPriceAtTick(tickUpper), uint128(100e18) ); (tokenId, ) = posm.mint( key, tickLower, tickUpper, 100e18, amount0 + 1, amount1 + 1, address(this), block.timestamp, hook.getHookData(address(this)) ); } function test_PointsHook_Swap() public { // [code here] } } ``` So far this test setup is fairly simple, we create a bunch of tokens and deploy v4 along with the position manager inside our test. Then, we create a pool with our hook and add some liquidity using the position manager. Now, let’s write our test. We’ll start by testing the points awarded during the swap. ```solidity function test_PointsHook_Swap() public { // We already have some points because we added some liquidity during setup. // So, we'll subtract those from the total points to get the points awarded for this swap. uint256 startingPoints = pointsToken.balanceOf(address(this)); // Let's swap some ETH for the token. bool zeroForOne = true; int256 amountSpecified = -1e18; // negative number indicates exact input swap! BalanceDelta swapDelta = swap( key, zeroForOne, amountSpecified, hook.getHookData(address(this)) ); uint256 endingPoints = pointsToken.balanceOf(address(this)); // Let's make sure we got the right amount of points! assertEq( endingPoints - startingPoints, uint256(-amountSpecified), "Points awarded for swap should be 1:1 with ETH" ); } ``` This test case is fairly straightforward and simply swaps 1 ETH for the target token and compares if we got the right amount of points awarded for it. Next, we should test our liquidity addition. ```solidity function test_PointsHook_Liquidity() public { // We already have some points because we added some liquidity during setup. // So, we'll subtract those from the total points to get the points awarded for this swap. uint256 startingPoints = pointsToken.balanceOf(address(this)); uint128 liqToAdd = 100e18; (uint256 amount0, uint256 amount1) = LiquidityAmounts .getAmountsForLiquidity( SQRT_PRICE_1_1, TickMath.getSqrtPriceAtTick(tickLower), TickMath.getSqrtPriceAtTick(tickUpper), liqToAdd ); posm.mint( key, tickLower, tickUpper, liqToAdd, amount0 + 1, amount1 + 1, address(this), block.timestamp, hook.getHookData(address(this)) ); uint256 endingPoints = pointsToken.balanceOf(address(this)); // Let's make sure we got the right amount of points! assertApproxEqAbs(endingPoints - startingPoints, uint256(liqToAdd), 10); } ``` This test case looks very similar to the `afterSwap` one, except we’re testing based on the liquidity added. You’ll notice at the end we’re testing for approximate equality within 10 points. This is to account for minor differences in actual liquidity added due to ticks and pricing. ## Next Steps Congratulations on building your very first hook! You could explore further by going to [Hook Deployment](./05-hook-deployment.mdx) to learn about how hook flags work and see how we will deploy a hook in action. --- ## Position Manager ## Introduction The Position Manager in v4 provides a streamlined way to manage liquidity positions through a command-based interface. Unlike previous versions where each operation required separate function calls, v4’s Position Manager uses a batched command pattern that allows multiple operations to be executed in a single transaction. ## Command-Based Design At its core, the Position Manager works by executing a sequence of commands: ```solidity // Example: Minting a new position requires two commands bytes memory actions = abi.encodePacked( Actions.MINT_POSITION, // Create the position Actions.SETTLE_PAIR // Provide the tokens ); ``` Each command (or action) represents a specific operation, and these actions can be: - Liquidity Operations: Creating, modifying, or removing positions - Delta-Resolving Operations: Handling token transfers and settlements ## How Commands Work Together When executing operations through the Position Manager, you’ll always: 1. Define what actions to perform 2. Provide the parameters for each action 3. Execute them through a single function call ```solidity // 1. Define actions bytes memory actions = abi.encodePacked(action1, action2); // 2. Encode parameters for each action bytes[] memory params = new bytes[](2); params[0] = abi.encode(/* parameters for action1 */); params[1] = abi.encode(/* parameters for action2 */); // 3. Execute through modifyLiquidities positionManager.modifyLiquidities( abi.encode(actions, params), deadline ); ``` This design enables efficient operations by: - Minimizing transaction costs through batching - Allowing complex position management in single transactions - Providing flexibility in how operations are combined In the following sections, we’ll explore how to work with these commands and implement common liquidity management operations. ## Core Concepts Before diving into specific operations, let’s understand the key concepts that make up the Position Manager’s architecture. ### Action Types The Position Manager operates through a system of actions that work in pairs: when you perform a liquidity operation that changes position balances, you must also include actions to handle the resulting token movements. ### Understanding the Flow When you execute a liquidity operation: 1. The operation creates deltas (token obligations) 2. These deltas represent tokens that need to be paid or received 3. Delta-resolving operations are then used to handle these token movements ### Liquidity Operations [Actions](/contracts/v4/reference/periphery/libraries/Actions) that modify positions in the pool: ```solidity // Common liquidity operations uint256 constant MINT_POSITION = 0x02; // Creates negative deltas (tokens needed for position) uint256 constant INCREASE_LIQUIDITY = 0x00; // Creates negative deltas (tokens to add) uint256 constant DECREASE_LIQUIDITY = 0x01; // Creates positive deltas (tokens to receive) uint256 constant BURN_6909 = 0x18; // Creates positive deltas (tokens to receive) ``` Each operation creates specific deltas that must be resolved: - Negative deltas when you need to provide tokens (mint, increase) - Positive deltas when you’re receiving tokens (decrease, burn) ### Delta-Resolving Operations Actions that handle the token transfers needed to resolve deltas: ```solidity // Common delta-resolving operations uint256 constant SETTLE_PAIR = 0x0d; // For negative deltas: Pay two tokens to the pool uint256 constant TAKE_PAIR = 0x11; // For positive deltas: Receive two tokens from the pool uint256 constant CLOSE_CURRENCY = 0x12; // Handles either direction based on final delta uint256 constant CLEAR_OR_TAKE = 0x13; // For small amounts: Take if worth it, else ignore ``` ### Operation Order Understanding how operations create and resolve deltas helps in ordering them efficiently: ```solidity // Efficient: Group operations that create deltas, then resolve them together bytes memory actions = abi.encodePacked( Actions.MINT_POSITION, // First delta: -100 tokens Actions.INCREASE_LIQUIDITY, // Second delta: -50 tokens Actions.SETTLE_PAIR // Resolve total: -150 tokens at once ); // Less efficient: Resolving deltas multiple times bytes memory actions = abi.encodePacked( Actions.MINT_POSITION, // Delta: -100 tokens Actions.SETTLE_PAIR, // Resolve: -100 tokens Actions.INCREASE_LIQUIDITY, // New delta: -50 tokens Actions.SETTLE_PAIR // Resolve again: -50 tokens ); ``` Best practices for ordering: 1. Group liquidity operations that create similar deltas (e.g., all negative or all positive) 2. Resolve all deltas together at the end when possible 3. Use `CLOSE_CURRENCY` when you can't predict the final delta ## Working with Liquidity Positions When building on v4, you’ll need to manage liquidity positions through the Position Manager. Let’s walk through each operation, starting with creating new positions. ### Minting New Positions To create a new liquidity position in v4, you’ll need to: 1. Define your position parameters (pool, price range, amount) 2. Mint the position NFT 3. Provide the initial tokens ### **Understanding Position Parameters** Before minting, you need to determine: - Which pool you’re providing liquidity to - Your price range (defined by tick bounds) - How much liquidity to provide - Maximum amounts of tokens you’re willing to spend ```solidity // Example position parameters PoolKey poolKey = // Your pool key int24 tickLower = -887272; // Price range lower bound int24 tickUpper = 887272; // Price range upper bound uint128 liquidity = 1000000; // Liquidity amount uint256 amount0Max = 1e18; // Max 1 token0 uint256 amount1Max = 1e18; // Max 1 token1 ``` ### **Implementation** Let’s implement a function to mint new liquidity positions step by step: ```solidity /// @notice Mints a new liquidity position /// @param poolKey The pool to provide liquidity to /// @param tickLower Lower bound of the price range /// @param tickUpper Upper bound of the price range /// @param liquidity Amount of liquidity to provide /// @param amount0Max Maximum amount of token0 to spend /// @param amount1Max Maximum amount of token1 to spend /// @param recipient Address that will own the position function mintNewPosition( PoolKey calldata poolKey, int24 tickLower, int24 tickUpper, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, address recipient ) external returns (uint256 tokenId) { ``` Define the sequence of operations needed for minting: ```solidity // Define the sequence of operations: // 1. MINT_POSITION - Creates the position and calculates token requirements // 2. SETTLE_PAIR - Provides the tokens needed bytes memory actions = abi.encodePacked( Actions.MINT_POSITION, Actions.SETTLE_PAIR ); ``` Set up parameters for each action: ```solidity bytes[] memory params = new bytes[](2); // Parameters for MINT_POSITION params[0] = abi.encode( poolKey, // Which pool to mint in tickLower, // Position's lower price bound tickUpper, // Position's upper price bound liquidity, // Amount of liquidity to mint amount0Max, // Maximum amount of token0 to use amount1Max, // Maximum amount of token1 to use recipient, // Who receives the NFT "" // No hook data needed ); // Parameters for SETTLE_PAIR - specify tokens to provide params[1] = abi.encode( poolKey.currency0, // First token to settle poolKey.currency1 // Second token to settle ); ``` Finally, execute the mint operation: ```solidity // Execute the mint operation positionManager.modifyLiquidities( abi.encode(actions, params), block.timestamp + 60 // 60 second deadline ); ``` ## Increasing Liquidity After a position is created, you might want to add more liquidity to it. This operation requires understanding how fee accumulation works since fees are credited to your position during an increase. ### **Understanding the Operation** When increasing liquidity: 1. The operation calculates the tokens needed based on current prices 2. Any accumulated fees are **automatically credited** to your position 3. In some cases, fee revenue might partially or fully cover the tokens needed ### **Choosing the Right Delta Resolution** Unlike minting where we always use SETTLE_PAIR, increasing liquidity has different delta-resolving options depending on your scenario: 1. **Standard Case**: When you’re providing new tokens ```solidity bytes memory actions = abi.encodePacked( Actions.INCREASE_LIQUIDITY, Actions.SETTLE_PAIR ); ``` **2. Fee Conversion**: When converting accumulated fees to liquidity ```solidity bytes memory actions = abi.encodePacked( Actions.INCREASE_LIQUIDITY, Actions.CLOSE_CURRENCY, // For token0 Actions.CLOSE_CURRENCY // For token1 ); ``` ### **Implementation** Here’s how to implement a flexible increase liquidity function: ```solidity /// @notice Increases liquidity in an existing position /// @param tokenId The ID of the position /// @param liquidityIncrease Amount of liquidity to add /// @param amount0Max Maximum amount of token0 to spend /// @param amount1Max Maximum amount of token1 to spend /// @param useFeesAsLiquidity Whether to use accumulated fees function increaseLiquidity( uint256 tokenId, uint128 liquidityIncrease, uint256 amount0Max, uint256 amount1Max, bool useFeesAsLiquidity ) external { ``` Choose the appropriate delta resolution based on whether we’re using fees: ```solidity // Define the sequence of operations: // If using fees: Handle potential fee conversion // If not: Standard liquidity addition bytes memory actions; if (useFeesAsLiquidity) { actions = abi.encodePacked( Actions.INCREASE_LIQUIDITY, // Add liquidity Actions.CLOSE_CURRENCY, // Handle token0 (might need to pay or receive) Actions.CLOSE_CURRENCY // Handle token1 (might need to pay or receive) ); } else { actions = abi.encodePacked( Actions.INCREASE_LIQUIDITY, // Add liquidity Actions.SETTLE_PAIR // Provide tokens ); } ``` Prepare parameters based on our chosen strategy: ```solidity // Number of parameters depends on our strategy bytes[] memory params = new bytes[]( useFeesAsLiquidity ? 3 : 2 ); // Parameters for INCREASE_LIQUIDITY params[0] = abi.encode( tokenId, // Position to increase liquidityIncrease, // Amount to add amount0Max, // Maximum token0 to spend amount1Max, // Maximum token1 to spend "" // No hook data needed ); ``` Set up delta resolution parameters: ```solidity if (useFeesAsLiquidity) { // Using CLOSE_CURRENCY for automatic handling of each token params[1] = abi.encode(currency0); // Handle token0 params[2] = abi.encode(currency1); // Handle token1 } else { // Standard SETTLE_PAIR for providing tokens params[1] = abi.encode(currency0, currency1); } ``` Execute the operation: ```solidity // Execute the increase positionManager.modifyLiquidities( abi.encode(actions, params), block.timestamp + 60 // 60 second deadline ); } ``` ## Decreasing Liquidity When you want to remove liquidity from a position, you’ll need to handle both the liquidity reduction and any accumulated fees. Let’s understand how to implement this effectively. ### **Understanding the Operation** Decreasing liquidity involves: 1. Specifying how much liquidity to remove 2. Setting minimum amounts to receive (slippage protection) 3. Collecting both the removed liquidity and any accumulated fees ### **Delta Resolution Options** When decreasing liquidity, you’ll receive tokens, so it's most common to receive a pair of tokens: ```solidity bytes memory actions = abi.encodePacked( Actions.DECREASE_LIQUIDITY, Actions.TAKE_PAIR ); ``` ### **Implementation** When removing liquidity from a position, you’ll be able to receive tokens and any accumulated fees. Let’s break down the implementation step by step. ```solidity /// @notice Removes liquidity from a position /// @param tokenId The ID of the position /// @param liquidityDecrease Amount of liquidity to remove /// @param amount0Min Minimum amount of token0 to receive /// @param amount1Min Minimum amount of token1 to receive /// @param recipient Address to receive the tokens function decreaseLiquidity( uint256 tokenId, uint128 liquidityDecrease, uint256 amount0Min, uint256 amount1Min, address recipient ) external { ``` Prepare the parameters array: ```solidity // Number of parameters depends on our strategy bytes[] memory params = new bytes[](2); // Parameters for DECREASE_LIQUIDITY params[0] = abi.encode( tokenId, // Position to decrease liquidityDecrease, // Amount to remove amount0Min, // Minimum token0 to receive amount1Min, // Minimum token1 to receive "" // No hook data needed ); ``` Set up delta resolution parameters: ```solidity // Parameters for TAKE_PAIR params[1] = abi.encode( currency0, currency1, recipient ); ``` Execute the operation: ```solidity // Execute the decrease positionManager.modifyLiquidities( abi.encode(actions, params), block.timestamp + 60 // 60 second deadline ); ``` ## Collecting Fees In v4’s Position Manager, there isn’t a dedicated COLLECT command. Instead, **fees are collected by using DECREASE_LIQUIDITY with zero liquidity**. This pattern leverages the fact that fees are automatically credited during liquidity operations. ### **Understanding Fee Collection** When collecting fees, you need to: 1. Perform a DECREASE_LIQUIDITY operation with zero liquidity 2. Handle the positive deltas (the fees you’re collecting) 3. Specify where the fees should go ### **Implementation** ```solidity /// @notice Collects accumulated fees from a position /// @param tokenId The ID of the position to collect fees from /// @param recipient Address that will receive the fees function collectFees( uint256 tokenId, address recipient ) external { // Define the sequence of operations bytes memory actions = abi.encodePacked( Actions.DECREASE_LIQUIDITY, // Remove liquidity Actions.TAKE_PAIR // Receive both tokens ); // Prepare parameters array bytes[] memory params = new bytes[](2); // Parameters for DECREASE_LIQUIDITY // All zeros since we're only collecting fees params[0] = abi.encode( tokenId, // Position to collect from 0, // No liquidity change 0, // No minimum for token0 (fees can't be manipulated) 0, // No minimum for token1 "" // No hook data needed ); ``` When collecting fees, we use a zero-liquidity decrease operation - this means we're not actually removing any liquidity from the position, we're just collecting accumulated fees. And note that we set minimums to 0 for fee collection because fees cannot be manipulated in a front-run attack. This is different from other liquidity operations where setting appropriate minimum amounts is crucial for slippage protection. Set up the fee collection parameters: ```solidity // Standard TAKE_PAIR for receiving all fees params[1] = abi.encode( currency0, currency1, recipient ); } ``` Execute the fee collection: ```solidity // Execute fee collection positionManager.modifyLiquidities( abi.encode(actions, params), block.timestamp + 60 // 60 second deadline ); ``` ## Burning Positions When you want to completely exit a position, burning is more efficient than removing liquidity and collecting fees separately. The BURN_POSITION command handles everything in a single operation. ### **Understanding Position Burning** A burn operation: - Removes all remaining liquidity from the pool - Collects any accumulated fees - Burns the position NFT - Settles all tokens to a specified recipient ### **Implementation** Let’s implement a position burning function step by step: ```solidity /// @notice Burns a position and receives all tokens /// @param tokenId The ID of the position to burn /// @param recipient Address that will receive the tokens /// @param amount0Min Minimum amount of token0 to receive /// @param amount1Min Minimum amount of token1 to receive function burnPosition( uint256 tokenId, address recipient, uint256 amount0Min, uint256 amount1Min ) external { // Define the sequence of operations: // 1. BURN_POSITION - Removes the position and creates positive deltas // 2. TAKE_PAIR - Sends all tokens to the recipient bytes memory actions = abi.encodePacked( Actions.BURN_POSITION, Actions.TAKE_PAIR ); ``` The burn operation requires two sets of parameters: ```solidity bytes[] memory params = new bytes[](2); // Parameters for BURN_POSITION params[0] = abi.encode( tokenId, // Position to burn amount0Min, // Minimum token0 to receive amount1Min, // Minimum token1 to receive "" // No hook data needed ); // Parameters for TAKE_PAIR - where tokens will go params[1] = abi.encode( currency0, // First token currency1, // Second token recipient // Who receives the tokens ); ``` Finally, execute the operation: ```solidity positionManager.modifyLiquidities( abi.encode(actions, params), block.timestamp + 60 ); ``` ## Batch-Operating Liquidity The Position Manager’s command-based design enables you to perform multiple liquidity operations in a single transaction. This is particularly valuable when managing multiple positions or performing complex liquidity management strategies, such as taking tokens from one position to increase liquidity of another position. ### Benefits of Batch Operations When managing liquidity across multiple positions, batching operations provides significant advantages: - Reduced gas costs by combining token settlements - Atomic execution of related operations - Simplified token handling through combined delta resolution ### Implementation Guide Let’s implement a common scenario: rebalancing liquidity by creating a new position while closing an old one and collecting its fees. We’ll go through it step by step. First, let’s define our parameters: ```solidity /// @notice Rebalances liquidity by creating a new position and closing an old one /// @param newPositionParams Parameters for the new position /// @param oldPositionId Position to close and collect fees from /// @param recipient Address to receive tokens from closed position struct NewPositionParams { PoolKey poolKey; int24 tickLower; int24 tickUpper; uint128 liquidity; uint256 amount0Max; uint256 amount1Max; } ``` In this example, we will rebalance liquidity by closing the old position and opening a new position. For the sake of example, let's assume the user will have to transfer additional tokens. Note that capital from the first position is automatically used towards the second position through flash accounting. ```solidity function rebalanceLiquidity( NewPositionParams calldata newPositionParams, uint256 oldPositionId, address recipient ) external { // Group liquidity operations first, then delta resolutions bytes memory actions = abi.encodePacked( Actions.BURN_POSITION, // Remove old position Actions.MINT_POSITION, // Create new position Actions.SETTLE_PAIR // Provide tokens for new position ); ``` Notice how we order our operations: liquidity operations first (MINT and BURN), followed by delta resolutions (SETTLE and TAKE). This ordering is crucial for gas efficiency. Next, let’s prepare our parameters array: ```solidity // We need one parameter set for each action bytes[] memory params = new bytes[](3); ``` Now, let’s encode parameters for the old position: ```solidity // Parameters for BURN_POSITION params[0] = abi.encode( oldPositionId, 0, // No minimum for token0 (consider adding slippage protection) 0, // No minimum for token1 "" // No hook data ); ``` Then for minting the new position: ```solidity // Parameters for MINT_POSITION params[1] = abi.encode( newPositionParams.poolKey, newPositionParams.tickLower, newPositionParams.tickUpper, newPositionParams.liquidity, newPositionParams.amount0Max, newPositionParams.amount1Max, address(this), // New position owner "" // No hook data ); ``` Next, we handle token settlements. First for the new position: ```solidity // Parameters for SETTLE_PAIR (providing tokens for new position) params[2] = abi.encode( newPositionParams.poolKey.currency0, newPositionParams.poolKey.currency1 ); ``` With everything prepared, we can execute our batch operation: ```solidity // Execute all operations atomically positionManager.modifyLiquidities( abi.encode(actions, params), block.timestamp + 60 ); positionManager.modifyLiquidities( abi.encode(actions, params), block.timestamp + 60 ); } ``` ## Delta-Resolving Operations While we’ve seen basic delta resolution using SETTLE_PAIR and TAKE_PAIR in previous sections, v4’s Position Manager provides additional operations for handling more complex scenarios. Let’s understand when and how to use each one. ### **CLOSE_CURRENCY: Handling Unknown Deltas** When you can’t predict whether you’ll need to pay or receive tokens, CLOSE_CURRENCY automatically handles either case. ```solidity // Example scenario: Converting fees to liquidity bytes memory actions = abi.encodePacked( Actions.INCREASE_LIQUIDITY, Actions.CLOSE_CURRENCY // Will automatically settle or take based on final delta ); bytes[] memory params = new bytes[](2); // Parameters for INCREASE_LIQUIDITY params[0] = abi.encode( tokenId, liquidityIncrease, amount0Max, amount1Max, "" ); // CLOSE_CURRENCY only needs the currency params[1] = abi.encode(currency0); ``` This is particularly useful when: - Converting fees to liquidity (fees might fully cover the increase) - Complex operations where final deltas are uncertain - Reducing code complexity by letting the protocol handle the direction ### **CLEAR_OR_TAKE: Optimizing for Dust** Sometimes receiving small token amounts costs more in gas than they’re worth. CLEAR_OR_TAKE lets you specify a threshold: ```solidity // Parameters for CLEAR_OR_TAKE params[0] = abi.encode( currency, // The token to handle threshold // Minimum amount worth taking ); ``` If the amount to receive is: - Above threshold: Tokens are taken (like TAKE_PAIR) - Below threshold: Amount is forfeited, saving gas This is valuable for: - Operations where dust amounts can be ignored - Gas optimization in production systems ### **SWEEP: Handling Excess Payments** SWEEP helps recover any excess tokens sent to the PoolManager: ```solidity bytes memory actions = abi.encodePacked( Actions.YOUR_MAIN_OPERATION, Actions.SWEEP // Add at the end to collect any excess ); // Parameters for SWEEP params[1] = abi.encode( currency, // Token to sweep recipient // Where to send excess tokens ); ``` Use SWEEP when: - **Dealing with native ETH operations** - Conservative token approvals might result in excess - Need to ensure all tokens are properly accounted for ### **Understanding modifyLiquiditiesWithoutUnlock** This function follows the same encoding and command patterns as `modifyLiquidity`, but serves a specific purpose: it's used when the PoolManager is already unlocked. This is particularly useful in certain scenarios: - When called from hooks that are already executing within the PoolManager's lock/unlock cycle - For operations like automatic fee compounding, where a hook might want to reinvest fees for users For example, a hook that automatically compounds fees for users would use `modifyLiquiditiesWithoutUnlock` because the hook is already executing within the PoolManager's unlock context, and cannot re-unlock the PoolManager --- ## Reading Pool State ## Introduction Unlike previous versions, v4 uses a different approach for storing and accessing pool data, which requires understanding the use of [`StateLibrary`](/contracts/v4/reference/core/libraries/StateLibrary) and [`extsload`](/contracts/v4/reference/core/Extsload). ## Understanding the PoolManager Architecture ### The Singleton Design In Uniswap v4, all pools are managed by a single `PoolManager` contract, unlike v3 where each pool was a separate contract. This design offers simplified management since all pools are now accessible through a single contract. This approach significantly reduces deployment costs, simplifies protocol upgrades, and enables more efficient cross-pool interactions. It also allows for easier implementation of new features across all pools simultaneously. ### Pools as Library Calls In v4, pools are stored as complex structs, with Solidity libraries handling state changes. The `PoolManager` contract uses these libraries to perform operations on the pool state: ```solidity contract PoolManager { using Pool for Pool.State; mapping(PoolId => Pool.State) internal pools; function swap(PoolId id, ...) external { pools[id].swap(...); // Library call } } ``` This design allows all AMM logic to be encapsulated within the `PoolManager` contract. ## Reading Pool State in v4 In Uniswap v4, reading pool state involves a few key concepts and mechanisms that differ from previous versions. At the core of this new structure is a complex mapping within the PoolManager contract: ```solidity mapping(PoolId id => Pool.State) internal _pools; ``` This mapping represents a fundamental shift in pool data storage: 1. Each pool is identified by a unique `PoolId`. 2. The `Pool.State` is a struct that contains all the state variables for a single pool. 3. This struct itself contains several nested mappings and complex data structures. For example, the `Pool.State` struct might look something like this (simplified for illustration): ```solidity struct State { uint160 sqrtPriceX96; int24 tick; uint128 liquidity; uint256 feeGrowthGlobal0X128; uint256 feeGrowthGlobal1X128; mapping(int24 => TickInfo) ticks; mapping(bytes32 => Position.Info) positions; // ... other fields } ``` This complex structure allows for efficient storage of multiple pools and their associated data within a single contract. However, it also means that traditional getter functions would be inefficient or impractical for accessing this data, especially for nested mappings like `ticks` and `positions`. To address this, Uniswap V4 introduces the StateLibrary and the concept of using `extsload` for reading pool state. These mechanisms provide efficient ways to access the data stored in this complex structure. ### The StateLibrary and `extsload` ```solidity abstract contract Extsload is IExtsload { /// @inheritdoc IExtsload function extsload(bytes32 slot) external view returns (bytes32) { assembly ("memory-safe") { mstore(0, sload(slot)) return(0, 0x20) } } // [...] } ``` The `StateLibrary` is a crucial component in Uniswap v4 for reading pool state. It utilizes the `extsload` function, which is an external wrapper for the `SLOAD` opcode. This allows for efficient reading of arbitrary storage slots. **How `extsload` works:** - It takes a storage slot as input. - It reads the value stored in that slot directly, using `SLOAD`, from the contract's storage. - It returns the value as a `bytes32`. This method is more gas-efficient than traditional getter functions, especially when reading multiple storage slots. Moreover, using `extsload` instead of hand-written Solidity view functions lowers the contract bytecode size. This optimization is particularly important for Uniswap v4, as the core contracts are nearly at Ethereum's contract size limit. ### TransientStateLibrary and `exttload` ```solidity abstract contract Exttload is IExttload { /// @inheritdoc IExttload function exttload(bytes32 slot) external view returns (bytes32) { assembly ("memory-safe") { mstore(0, tload(slot)) return(0, 0x20) } } // [...] } ``` While `StateLibrary` deals with persistent storage, [`TransientStateLibrary`](/contracts/v4/reference/core/libraries/transient-state-library) is used for handling transient storage. Transient storage, introduced in EIP-1153, is a way to store data that is only needed for the duration of a transaction, making it ideal for temporary data. It uses the [`exttload`](/contracts/v4/reference/core/Exttload) function, which is similar to `extsload`, but for transient storage; it is an external wrapper for the `TLOAD` opcode. ## Implementing a `PoolStateReader` Contract Let's create a `PoolStateReader` contract that showcases different methods for reading pool state. For each function, we'll explain its purpose, how it works, and provide an example use case. ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.26; contract PoolStateReader { using PoolIdLibrary for PoolKey; IPoolManager public immutable poolManager; constructor(IPoolManager _poolManager) { poolManager = _poolManager; } // Functions will be implemented here } ``` Before we start, we need to import `StateLibrary` from the libraries available in v4-core. ```solidity ``` Let's focus on this important line that we should add: ```solidity using StateLibrary for IPoolManager; ``` This line is crucial for our PoolStateReader contract because it allows us to call StateLibrary functions as if they were methods of the IPoolManager interface, like for instance now we will be able to do `poolManager.getSlot0()`. Now we’re up for breaking down each wrapper function that we're going to be adding to our helper contract, explain the purpose of the pool manager function to read the state, and provide use cases to make sure we understand its utility: ### `getSlot0()` ```solidity function getPoolState(PoolKey calldata key) external view returns ( uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee ) { return poolManager.getSlot0(key.toId()); } ``` **Explanation:** This function retrieves the current state of the pool, including its price, tick, and fee settings. It uses the `getSlot0()` function from `StateLibrary`, which efficiently reads these values from a single storage slot. - `sqrtPriceX96`: The current price, encoded as a square root and scaled by 2^96. This encoding allows for efficient price calculations in the Uniswap algorithm. - `tick`: The current tick, representing the quantized price. Ticks are used to efficiently track and update liquidity positions. - `protocolFee`: The current protocol fee, represented in hundredths of a bip (i.e., units of 0.0001%). - `lpFee`: The current liquidity provider fee, also represented in hundredths of a bip. **Use Case:** This function is essential for any application that needs to know the current state of a Uniswap v4 pool. For example: - A price oracle could use this to get the current price of the pool. - A trading bot could use this to determine if a trade is profitable given the current price and fees. - A liquidity management system could use the `tick` to decide where to place new liquidity. ### `getLiquidity()` ```solidity function getPoolLiquidity(PoolKey calldata key) external view returns (uint128 liquidity) { return poolManager.getLiquidity(key.toId()); } ``` **Explanation:** This function retrieves the current total liquidity in the pool. Liquidity in Uniswap v4 represents the amount of assets available for trading within the current price range. **Use Case:** Understanding the total liquidity is crucial for several scenarios: - Assessing the depth of the market and potential slippage for large trades. - Monitoring the depth and growth of a pool over time. ### `getPositionInfo` ```solidity function getPositionInfo( PoolKey calldata key, address owner, int24 tickLower, int24 tickUpper, bytes32 salt ) external view returns ( uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128 ) { return poolManager.getPositionInfo(key.toId(), owner, tickLower, tickUpper, bytes32(salt)); } ``` **Explanation:** This function retrieves information about a specific liquidity position. It returns: - `liquidity`: The amount of liquidity in the position. - `feeGrowthInside0LastX128` and `feeGrowthInside1LastX128`: The last recorded cumulative fees earned per unit of liquidity for each token. **Use Case:** This function is crucial for applications that need to manage or analyze individual liquidity positions: - A liquidity management dashboard could use this to display a user's current positions and earned fees. - An automated liquidity provision system could use this to decide when to rebalance or compound rewards. - An analytics tool could use this to calculate the performance of different liquidity provision strategies. ### `getFeeGrowthGlobal` ```solidity function getFeeGrowthGlobal(PoolKey calldata key) external view returns ( uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128 ) { return poolManager.getFeeGrowthGlobal(key.toId()); } ``` **Explanation:** This function retrieves the global fee growth for both tokens in the pool. These values represent the cumulative fees per unit of liquidity since the pool's inception. **Use Case:** Global fee growth is essential for several advanced operations: - Calculating the fees earned by a position that has been held for a long time. - Analyzing the overall fee generation of a pool over its lifetime. - Comparing the performance of different pools or fee tiers. --- For additional reference, see [`StateLibrary`](/contracts/v4/reference/core/libraries/StateLibrary) and [`Extsload`](/contracts/v4/reference/core/Extsload) --- ## StateView ## Introduction When building on **Uniswap v4**, you will often need to read pool state for both onchain and offchain use cases. Onchain contracts can directly invoke the [**StateLibrary**](https://github.com/Uniswap/v4-core/blob/main/src/libraries/StateLibrary.sol) to execute these reads during transactions, but offchain systems—such as frontends or analytics services—require a deployed contract with view functions. This is where [**StateView**](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) comes in. > *In short: Use StateLibrary within onchain contracts and use StateView with an RPC for frontends and analytics.* > By providing a dedicated interface for offchain reads, **StateView** helps: - Retrieve pool state without paying gas - Simplify integration for frontends, dashboards, and analytics - Ensure a clean separation between onchain logic and offchain queries ## Comparing onchain and offchain Access If you’re familiar with [Reading Pool State](/contracts/v4/guides/read-pool-state), you already know that Uniswap v4 uses **extsload** for efficient data access. For onchain usage, we rely on **StateLibrary** within contracts. However, offchain clients cannot rely on an onchain library for state reads. Instead, **StateView** provides these same calls in a single contract designed explicitly for offchain consumption. > *Because StateLibrary operates via onchain function calls, it’s not directly accessible to offchain clients. Hence, StateView provides a simple, gas-free interface designed for frontends and analytics.* > For instance, an onchain contract might use the `StateLibrary` as follows: ```solidity // Onchain contract using StateLibrary contract MyProtocol { using StateLibrary for IPoolManager; function checkPoolPrice(PoolId poolId) external returns (uint160) { (uint160 sqrtPriceX96, , , ) = poolManager.getSlot0(poolId); // ... use the price in contract logic ... return sqrtPriceX96; } } ``` By contrast, an offchain frontend or analytics service should interact with `StateView`: ```tsx // Frontend or analytics client using StateView const stateView = getContract({ address: STATE_VIEW_ADDRESS, abi: stateViewABI }); const { sqrtPriceX96 } = await stateView.read.getSlot0([poolId]); // ... use the price in your application ... ``` This separation ensures that each context (onchain vs. offchain) uses the most efficient data reading pattern. ## Usage With Frontend Clients Frontend applications frequently display real-time information about pools, positions, and other market data—without incurring transaction costs. **StateView** addresses these requirements by exposing read-only functions tailored for offchain integrations. ### Setting Up With Viem We’ll use [**viem**](https://viem.sh/), a TypeScript library for Ethereum, to demonstrate how to connect to **StateView**. ```tsx // Initialize the client const client = createPublicClient({ chain: mainnet, transport: http() }); // Set up StateView contract instance const stateView = getContract({ address: STATE_VIEW_ADDRESS, abi: stateViewABI, client }) ``` > **Note:** _The stateView object comes from our getContract call above. Make sure you’ve imported stateViewABI correctly before attempting to read from the contract._ > With this setup, you can now: - **Connect to an Ethereum network** - **Call StateView’s read functions** - **Retrieve pool information offchain at no gas cost** ### Handling Errors and Invalid Pool IDs When calling `stateView.read.([poolId])`, be mindful that: - If you pass an invalid `poolId` (typically a [`bytes32`](https://github.com/Uniswap/v4-core/blob/main/src/types/PoolId.sol#L6) in Uniswap v4), the call may revert or return unexpected data. - Consider adding try-catch (or equivalent error handling in your framework) to gracefully handle failures if the pool does not exist or if the call fails onchain. ## Reading Pool Data Here are common examples of how to retrieve pool data using **StateView**. ### Getting Pool State A pool’s core state, such as its current price or fees, is often necessary for frontends. Use `getSlot0`: ```tsx // Example: Reading pool price and fees const getPoolState = async (poolId: string) => { // getSlot0 returns: // - Current price (sqrtPriceX96) in Q64.96 fixed-point format // - Active tick // - Protocol and LP fee settings const [ sqrtPriceX96, tick, protocolFee, lpFee ] = await stateView.read.getSlot0([poolId]); return { price: calculatePrice(sqrtPriceX96), // implement your math logic for Q64.96 tick, protocolFee, lpFee }; }; ``` **What it Returns:** - **`sqrtPriceX96`**: The current pool price in Q64.96 fixed-point format. - **`tick`**: The current tick in which the pool is operating. - **`protocolFee`** and **`lpFee`**: Fee parameters for protocol and LP fee tiers. ### Getting Pool Liquidity To understand how much liquidity a pool holds: ```tsx // Example: Reading the total active liquidity of a pool const getPoolLiquidity = async (poolId: string) => { // getLiquidity returns the total liquidity currently active in the pool const liquidity = await stateView.read.getLiquidity([poolId]); return liquidity; }; ``` **Why It Matters:** - Helps gauge the depth of the pool - Influences price impact calculations in trading - Provides context for the pool’s capacity to absorb trades ## Core Functions and Return Types While **StateView** exposes many functions, here are several essential calls for most offchain applications. Each function typically takes a `poolId` (of type `bytes32`) as the key input, identifying which pool to query. 1. **[`getSlot0(poolId)`](/contracts/v4/reference/periphery/lens/StateView#getslot0)** - Returns `(uint160 sqrtPriceX96, int24 tick, uint8 protocolFee, uint8 lpFee)`. - Essential for displaying real-time price data and fees. 2. **[`getLiquidity(poolId)`](/contracts/v4/reference/periphery/lens/StateView#getliquidity)** - Returns `uint128 liquidity` (the total active pool liquidity). - Used to assess trading depth and volatility. 3. **[`getPositionInfo(poolId, positionId)`](/contracts/v4/reference/periphery/lens/StateView#getpositioninfo)** - Returns `(uint128 liquidity, uint256 feeGrowthInside0Last, uint256 feeGrowthInside1Last)`. - Critical for tracking user positions, especially to calculate earned fees over time. 4. **[`getFeeGrowthGlobals(poolId)`](/contracts/v4/reference/periphery/lens/StateView#getfeegrowthglobals)** - Returns `(uint256 feeGrowthGlobal0, uint256 feeGrowthGlobal1)`. - Useful for analytics around total fee accumulation in the pool. ### Note on `poolId` and `positionId` - In **Uniswap v4**, a `poolId` is typically a `bytes32` that is derived by calling `keccak256(abi.encode(poolKey))` where poolKey contains: - currency0: The lower currency address of the pool - currency1: The higher currency address of the pool - fee: The pool LP fee (uint24) - tickSpacing: The tick spacing value (int24) - hooks: The hooks contract address - A `positionId` may also be a `bytes32` or other unique identifier that references a specific position. ## Security and Gas Considerations - **Offchain Reads**: Calls to `StateView` are purely read-only, so they cost no gas. This makes them ideal for frequently refreshing UI/analytics data. - **Onchain vs. Offchain**: Remember that if you need to integrate pool data into a live transaction, you must use `StateLibrary` within your smart contract. - **Edge Cases**: Always verify the returned data before using it in your application. Network or contract errors could lead to unexpected values. ## Conclusion **StateView** is a powerful and efficient way to read Uniswap v4 pool data offchain. By separating onchain logic (using `StateLibrary`) and offchain reads (using `StateView`), Uniswap ensures the best developer experience for both contexts. To recap: 1. **Setup**: Use libraries like `viem` to connect to the Ethereum network. 2. **Read**: Call `getSlot0`, `getLiquidity`, `getPositionInfo`, and other methods for crucial state data. 3. **Handle Errors**: Implement basic checks for invalid `poolId` or connection failures. --- ## Swap routing ## Introduction to Universal Router for Uniswap v4 Swaps Uniswap v4 introduces a new architecture where all pools are managed by a single PoolManager contract. While the underlying architecture uses a callback system for swaps, developers can still use the Universal Router to execute swaps on v4 pools, just as you would for v2 or v3. ## What is the Universal Router? The Universal Router is a flexible, gas-efficient contract designed to execute complex swap operations across various protocols, including Uniswap v4. It serves as an intermediary between users and the Uniswap v4 `PoolManager`, handling the intricacies of swap execution. While it’s technically possible to interact directly with the PoolManager contract for swaps, this approach is generally not recommended due to its complexity and potential inefficiencies. The Universal Router is designed to abstract away these complexities, providing a more straightforward and efficient method for executing swaps on v4 pools. ## UniversalRouter command encoding The [Universal Router](/contracts/universal-router/overview) uses a unique encoding system for its commands and inputs, which is crucial to understand when configuring it for v4 swaps. When calling `UniversalRouter.execute()`, you provide two main parameters: 1. `bytes commands`: A string of bytes where each byte represents a single command to be executed. 2. `bytes[] inputs`: An array of byte strings, each containing the encoded parameters for its corresponding command. The `commands[i]` byte corresponds to the `inputs[i]` parameters, allowing for a series of operations to be defined and executed in sequence. Each command is encoded as a single byte (`bytes1`) with a specific structure: ``` 0 1 2 3 4 5 6 7 ┌─┬─┬───────────┐ │f│r| command │ └─┴─┴───────────┘ ``` - The first bit (`f`) is a flag that determines whether the command is allowed to revert without causing the entire transaction to fail. This enables partial execution of complex transactions. - The second bit (`r`) is reserved for future use, providing flexibility for potential upgrades. - The remaining 6 bits represent the specific command to be executed. ## Configuring Universal Router for Uniswap v4 Swaps ### Use Cases Developers might need to configure the Universal Router for swapping on Uniswap v4 pools in several scenarios: 1. **Building a DEX aggregator**: If you’re creating a platform that finds the best rates across multiple DEXes, you’ll want to include Uniswap v4 pools in your options. 2. **Developing a trading bot**: Automated trading strategies often require the ability to execute swaps programmatically across various pools and versions. 3. **Creating a Dapp**: Many DeFi applications (lending platforms, yield aggregators, etc.) need to perform token swaps as part of their core functionality. This guide focuses on how to interact with Universal Router from an on-chain contract. ### Step 1: Set Up the Project First, we need to set up our project and install the necessary dependencies. ```bash forge install uniswap/v4-core forge install uniswap/v4-periphery forge install uniswap/permit2 forge install uniswap/universal-router forge install OpenZeppelin/openzeppelin-contracts ``` In the `remappings.txt`, add the following: ``` @uniswap/v4-core/=lib/v4-core/ @uniswap/v4-periphery/=lib/v4-periphery/ @uniswap/permit2/=lib/permit2/ @uniswap/universal-router/=lib/universal-router/ @openzeppelin/contracts/=lib/openzeppelin-contracts/ [...] ``` We’ll create a new Solidity contract for our example. ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.26; contract Example { using StateLibrary for IPoolManager; IUniversalRouter public immutable router; IPoolManager public immutable poolManager; IPermit2 public immutable permit2; constructor(address _router, address _poolManager, address _permit2) { router = IUniversalRouter(_router); poolManager = IPoolManager(_poolManager); permit2 = IPermit2(_permit2); } // We'll add more functions here } ``` In this step, we’re importing the necessary contracts and interfaces: - `UniversalRouter`: This will be our main interface for executing swaps. It provides a flexible way to interact with various Uniswap versions and other protocols. - `Commands`: This library contains the command definitions used by the UniversalRouter. - `IPoolManager`: This interface is needed for interacting with Uniswap v4 pools. While we don't directly use it in our simple example, it's often necessary for more complex interactions with v4 pools. - `IPermit2`: This interface allows us to interact with the Permit2 contract, which provides enhanced token approval functionality. - `StateLibrary`: This provides optimized functions for interacting with the PoolManager’s state. By using `StateLibrary`, we can more efficiently read and manipulate pool states, which is crucial for many operations in Uniswap v4. ### Step 2: Implement Token Approval with Permit2 `UniversalRouter` integrates with [Permit2](https://github.com/Uniswap/permit2), to enable users to have more safety, flexibility, and control over their ERC20 token approvals. Before we can execute swaps, we need to ensure our contract can transfer tokens. We’ll implement a function to approve the Universal Router to spend tokens on behalf of our contract. Here, for testing purposes, we set up our contract to use Permit2 with the UniversalRouter: ```solidity function approveTokenWithPermit2( address token, uint160 amount, uint48 expiration ) external { IERC20(token).approve(address(permit2), type(uint256).max); permit2.approve(token, address(router), amount, expiration); } ``` This function first approves Permit2 to spend the token, then uses Permit2 to approve the UniversalRouter with a specific amount and expiration time. ### Step 3: Implementing a Swap Function #### 3.1: Function Signature First, let’s define our function signature: ```solidity function swapExactInputSingle( PoolKey calldata key, // PoolKey struct that identifies the v4 pool uint128 amountIn, // Exact amount of tokens to swap uint128 minAmountOut, // Minimum amount of output tokens expected uint256 deadline // Timestamp after which the transaction will revert ) external returns (uint256 amountOut) { // Implementation will follow } ``` **Important note:** 1. The deadline parameter allows users to specify when their transaction should expire. This protects against unfavorable execution due to network delays or MEV attacks. 2. When swapping tokens involving native ETH, we use `Currency.wrap(address(0))` to represent ETH in the `PoolKey` struct. ```solidity struct PoolKey { /// @notice The lower currency of the pool, sorted numerically. /// For native ETH, Currency currency0 = Currency.wrap(address(0)); Currency currency0; /// @notice The higher currency of the pool, sorted numerically Currency currency1; /// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1, the pool has a dynamic fee and must be exactly equal to 0x800000 uint24 fee; /// @notice Ticks that involve positions must be a multiple of tick spacing int24 tickSpacing; /// @notice The hooks of the pool IHooks hooks; } ``` #### 3.2: Encoding the Swap Command When encoding a swap command for the Universal Router, we need to choose between two types of swaps: 1. Exact Input Swaps: Use this swap-type when you know the exact amount of tokens you want to swap in, and you're willing to accept any amount of output tokens above your minimum. This is common when you want to sell a specific amount of tokens. 2. Exact Output Swaps: Use this swap-type when you need a specific amount of output tokens, and you're willing to spend up to a maximum amount of input tokens. This is useful when you need to acquire a precise amount of tokens, for example, to repay a loan or meet a specific requirement. Next, we encode the swap command: ```solidity bytes memory commands = abi.encodePacked(uint8(Commands.V4_SWAP)); ``` Here, we're using `V4_SWAP`, which tells the Universal Router that we want to perform a swap on a Uniswap v4 pool. The specific type of swap (exact input or exact output) will be determined by the V4Router actions we encode later. As we saw earlier, we encode this as a single byte, which is how the Universal Router expects to receive commands. Check the complete list of [commands](https://docs.uniswap.org/contracts/universal-router/technical-reference#command). #### 3.3: Action Encoding Now, let’s encode the actions for the swap: ```solidity // Encode V4Router actions bytes memory actions = abi.encodePacked( uint8(Actions.SWAP_EXACT_IN_SINGLE), uint8(Actions.SETTLE_ALL), uint8(Actions.TAKE_ALL) ); ``` These actions define the sequence of operations that will be performed in our v4 swap: 1. `SWAP_EXACT_IN_SINGLE`: This action specifies that we want to perform an exact input swap using a single pool. 2. `SETTLE_ALL`: This action ensures all input tokens involved in the swap are properly paid. This is part of v4's settlement pattern for handling token transfers. 3. `TAKE_ALL`: This final action collects all output tokens after the swap is complete. The sequence of these actions is important as they define the complete flow of our swap operation from start to finish. #### 3.4: Preparing the Swap Inputs For our v4 swap, we need to prepare three parameters that correspond to our encoded actions: ```solidity bytes[] memory params = new bytes[](3); // First parameter: swap configuration params[0] = abi.encode( IV4Router.ExactInputSingleParams({ poolKey: key, zeroForOne: true, // true if we're swapping token0 for token1 amountIn: amountIn, // amount of tokens we're swapping amountOutMinimum: minAmountOut, // minimum amount we expect to receive hookData: bytes("") // no hook data needed }) ); // Second parameter: specify input tokens for the swap // encode SETTLE_ALL parameters params[1] = abi.encode(key.currency0, amountIn); // Third parameter: specify output tokens from the swap params[2] = abi.encode(key.currency1, minAmountOut); ``` Each encoded parameter serves a specific purpose: 1. The first parameter configures how the swap should be executed, defining the pool, amounts, and other swap-specific details 2. The second parameter defines what tokens we're putting into the swap 3. The third parameter defines what tokens we expect to receive from the swap These parameters work in conjunction with the actions we encoded earlier (`SWAP_EXACT_IN_SINGLE`, `SETTLE_ALL`, and `TAKE_ALL`) to execute our swap operation. #### 3.5: Executing the Swap Now we can execute the swap using the Universal Router. It's crucial to allow users to specify their own deadline for transaction execution: ```solidity // Combine actions and params into inputs inputs[0] = abi.encode(actions, params); // Execute the swap with deadline protection router.execute(commands, inputs, deadline); ``` This prepares and executes the swap based on our encoded commands, actions, and parameters. > **Note**: Never use block.timestamp or type(uint256).max as the deadline parameter. > #### 3.6: (Optional) Verifying the Swap Output After the swap, we need to verify that we received at least the minimum amount of tokens we specified: ```solidity amountOut = IERC20(Currency.unwrap(key.currency1)).balanceOf(address(this)); require(amountOut >= minAmountOut, "Insufficient output amount"); ``` #### 3.7: Returning the Result Finally, we return the amount of tokens we received: ```solidity return amountOut; ``` This allows the caller of the function to know exactly how many tokens were received in the swap. Here's the complete swap function that combines all the steps we've covered: ```solidity function swapExactInputSingle( PoolKey calldata key, uint128 amountIn, uint128 minAmountOut, uint256 deadline ) external returns (uint256 amountOut) { // Encode the Universal Router command bytes memory commands = abi.encodePacked(uint8(Commands.V4_SWAP)); bytes[] memory inputs = new bytes[](1); // Encode V4Router actions bytes memory actions = abi.encodePacked( uint8(Actions.SWAP_EXACT_IN_SINGLE), uint8(Actions.SETTLE_ALL), uint8(Actions.TAKE_ALL) ); // Prepare parameters for each action bytes[] memory params = new bytes[](3); params[0] = abi.encode( IV4Router.ExactInputSingleParams({ poolKey: key, zeroForOne: true, amountIn: amountIn, amountOutMinimum: minAmountOut, hookData: bytes("") }) ); params[1] = abi.encode(key.currency0, amountIn); params[2] = abi.encode(key.currency1, minAmountOut); // Combine actions and params into inputs inputs[0] = abi.encode(actions, params); // Execute the swap router.execute(commands, inputs, deadline); // Verify and return the output amount amountOut = IERC20(Currency.unwrap(key.currency1)).balanceOf(address(this)); require(amountOut >= minAmountOut, "Insufficient output amount"); return amountOut; } ``` --- ## Unlock Callback & Deltas ### Refresher In order to have access to the liquidity inside the `PoolManager`, it needs to be _unlocked_ to begin with. After being unlocked, any number of operations can be executed, which at the end of must be _locked_ again. At this point, if there are any _non-zero deltas_, meaning the PoolManager is owed or owes tokens back to some address, the whole execution reverts. Otherwise, both parties have paid or received the right amount of tokens and the operations have successfully carried out. ## Unlocking the PoolManager ### Implementing the unlock callback Prior to unlocking the PoolManager, the integrating contract must implement the `unlockCallback` function. This function will be called by the PoolManager after being unlocked. An easy way to do this is to inherit the `SafeCallback` abstract contract. ```solidity contract IntegratingContract is SafeCallback { constructor(IPoolManager _poolManager) SafeCallback(_poolManager) {} } ``` ### Calling the unlock function After implementing the callback, the integrating contract can now invoke the `unlock()` function. It receives a _bytes_ parameter that is further passed to your callback function as an argument. This parameter is used to encode the sequence of operations to be executed in the context of the `PoolManager`. ```solidity bytes memory unlockData = abi.encode(encode_operations_here); bytes memory unlockResultData = poolManager.unlock(unlockData); ``` Next, we must override the `_unlockCallback` function inherited from the `SafeCallback` contract. In your implementation, you should decode your operations and continue with the desired logic. ```solidity function _unlockCallback(bytes calldata data) internal override returns (bytes memory) { (...) = abi.decode(data, (...)); } ``` ## Operations There are **9** operations that can be done in the `PoolManager` which fall in two categories: _liquidity-accessing_ and _delta-resolving_. ### Deltas Deltas are the `PoolManager`'s method to keep track of token amounts it needs to receive, respectively to distribute. A negative delta signals that the `PoolManager` is owed tokens, while a positive one expresses a token balance that needs to be paid to its user. ### Liquidity-accessing _Liquidity-accessing_ operations will create non-zero _deltas_ and produce a state transition of the selected pool. They are the following: * _modify liquidity_ - used to increase or decrease liquidity; increasing liquidity will result in a negative token delta, while decreasing yields a positive one * _swap_ - used to trade one token for another; will result in a negative tokenA delta and a positive tokenB delta * _donate_ - used to provide direct token revenue to positions in range; will result in a negative delta for the pool's tokens the user wishes to provide ### Delta-resolving _Delta-resolving_ operations are used to even out the deltas created by the _liquidity-accessing_ operations. They are the following: * _settle_ - used following token transfers to the manager or burning of ERC6909 claims to resolve negative deltas * _take_ - transfer tokens from the manager, used to resolve positive deltas but also provide token loans, producing negative deltas * _mint_ - used to create ERC6909 claims, creating a negative delta that needs to be resolved by transferring the corresponding token and _settling_ afterwards * _burn_ - removes ERC6909 claims, creating a positive delta for tokens to be transferred back to the owner or used in settling negative balances * _clear_ - used to zero out positive token deltas, helpful to forfeit insignificant token amounts in order to avoid paying further transfer costs ### Handling Deltas for Liquidity Modifications #### When it happens - **Building custom routers** that pre-calculate token amounts. - **Estimating values** for user interfaces or simulations. #### Why It Happens - **Pre-calculated amounts** (e.g., from `LiquidityAmounts.getAmountsForLiquidity()`) use static math. - **Actual deltas** (from `modifyLiquidity()`) reflect real-time pool state, including: - Tick crossings during execution. - Rounding in fixed-point arithmetic (`Q128.128`). #### Why LiquidityAmounts ≠ Liquidity Delta The discrepancy occurs because: - **Price Movement:** The pool's price changes between pre-calculation and execution - **Tick Crossings:** Transactions may cross ticks, changing liquidity math - **Rounding:** Static calculations use idealized math while execution uses Q128.128 fixed-point #### 📊 Price Movement Example When ETH/USDC price changes during transaction execution: ```solidity // Static math calculation LiquidityAmounts.getAmountsForLiquidity( sqrtRatioX96: 3000, // Fixed price ... ); // Interacts with the pool and uses actual execution (reflects real-time price) poolManager.modifyLiquidity( sqrtRatioX96: 3001, // Updated price ... ); ``` getAmountsForLiquidity() assumes static 3000 price modifyLiquidity() reflects actual 3001 price #### Key Impact | Scenario | Risk | |----------|------| | **Underestimating deltas** | Transactions revert with `CurrencyNotSettled`. | | **Overestimating deltas** | Users overpay and lose funds to residual dust. | | **No slippage check** | Significant financial losses. | #### Best Practices for Custom Routers 1. **Never settle without validating against slippage** Supposing slippage tolerance is 50 (basis point) ```solidity require( actualAmount0 >= expectedAmount0 * (10_000 - slippageTolerance) / 10_000, "Slippage too high (token0)" ); require( actualAmount1 >= expectedAmount1 * (10_000 - slippageTolerance) / 10_000, "Slippage too high (token1)" ); ``` 2. **Use Deltas for Settlement** Always derive final amounts from `modifyLiquidity()` deltas: ```solidity CallbackData memory _data = abi.decode(data, (CallbackData)); (BalanceDelta delta, ) = poolManager.modifyLiquidity( _data.key, _data.params, hex"" ); _data.key.currency0.settle(poolManager, _data.key.hookAddress, delta.amount0() < 0 ? uint256(uint128(-delta.amount0())) : uint256(uint128(delta.amount0())), false); _data.key.currency1.settle(poolManager, _data.key.hookAddress, delta.amount1() < 0 ? uint256(uint128(-delta.amount1())) : uint256(uint128(delta.amount1())), false); ``` > ⚠️ **Custom Router Pitfall** > When pre-calculating liquidity changes, always account for rounding differences. > **Never** assume `getAmountsForLiquidity() == modifyLiquidity()` deltas. > Enforce slippage post-execution. --- ## Overview(V4) Uniswap v4 inherits all of the capital efficiency gains of Uniswap v3, but provides flexibility via *hooks* and gas optimizations across the entire lifecycle. For additional information, see the [Uniswap v4 whitepaper](https://app.uniswap.org/whitepaper-v4.pdf) ## Hooks Developers can attach solidity logic to the _swap lifecycle_ through Hooks. The logic is executed before and/or after major operations such as pool creation, liquidity addition and removal, swapping, and donations. Hooks are deployed contracts, and are called by the Uniswap v4 PoolManager, for permissionless execution. The flexibility of hooks can enable: * Limit orders * Custom oracles * Fee management * Automated liquidity management ## Dynamic Fees Uniswap v4 supports dynamic fees, allowing pools to adjust their fees up or down. While other AMMs may have hard-coded logic for dynamic fees, v4 provides no opinionated calculation of the fee. The frequency of *liquidity fee* updates is also flexible and determined by the developer. Fee updates can occur on every swap, every block, or on an arbitrary schedule (weekly, monthly, yearly, etc). Dynamic fees open up the design space for fee optimization, value redistribution, and research. ## Singleton Design Architecturally, all pool state and operations are managed by a single contract -- `PoolManager.sol`. The singleton design provides major gas savings. For example, creating a pool is now a state update instead of the deployment of a new contract. Swapping through multiple pools no longer requires transferring tokens for intermediate pools. ## Flash Accounting By leveraging EIP-1153 Transient Storage, v4 provides an optimization referred to as *flash accounting*. Swapping, liquidity modification, and donations incur *balance changes*, i.e. tokens to be sent in and tokens to be taken out. With *flash accounting* these balance changes are efficiently recorded in transient storage and netted against each other. This system allows users to only pay the final balance change, without the need for resolving intermediate balance changes. ## Native ETH Uniswap v4 supports native token assets (Ether), without the need to wrap/unwrap the native token to Wrapped Ether (WETH9). ## Custom Accounting The flexibility of custom accounting allows developers to alter token amounts for swaps and liquidity modifications. The feature opens up the design space for hooks to charge fees or forgo the underlying concentrated liquidity model. Example use-cases: * Custom curves, opt-out of the concentrated liquidity curve in favor of an entirely independent pricing mechanism * Hook swap fees, charge and collect fees on swaps * Liquidity withdrawal fees, penalize and/or redistribute fee revenue --- ## Create Pool ## Context 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 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 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](/contracts/v4/reference/periphery/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( IPoolInitializer_v4.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 // Note: In production, deadlines should be calculated off-chain using real time // For example: uint256 deadline = block.timestamp + 3600; // 1 hour from now uint256 deadline = block.timestamp + 3600; // 1 hour deadline 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) --- ## AsyncSwap Hooks One feature enabled by [custom accounting](/contracts/v4/guides/custom-accounting) is​​​​‌ AsyncSwap swap. This feature allows hook developers to replace the v4 (v3-style) swap logic. This means developers can replace Uniswap's internal core logic for how to handle swaps. Two emergent use-cases are possible with custom accounting: 1. Asynchronous swaps and swap-ordering. Delay the v4 swap logic for fulfillment at a later time. 2. Custom Curves. Replace the v4 swap logic with different swap logic. The custom logic is flexible and developers can implement symmetric curves, asymmetric curves, or custom quoting. > AsyncSwap is typically described as taking the full input to replace the internal swap logic, partially taking the input is better described as *custom accounting* Note: The flexibility of AsyncSwap means hook developers can implement harmful behavior (such as taking all swap amounts for themselves, charging extra fees, etc.). Hooks with AsyncSwap behavior should be examined very closely by both developers and users. ## Configure a AsyncSwap Hook To enable AsyncSwap, developers will need the hook permission `BEFORE_SWAP_RETURNS_DELTA_FLAG` ```solidity // ... contract AsyncSwapHook is BaseHook { // ... function getHookPermissions() public pure virtual override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, beforeAddLiquidity: false, afterAddLiquidity: false, beforeRemoveLiquidity: false, afterRemoveLiquidity: false, beforeSwap: false, afterSwap: true, beforeDonate: false, afterDonate: false, beforeSwapReturnDelta: true, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } // ... } ``` ## beforeSwap AsyncSwap only works on exact-input swaps and the *beforeSwap* **must** take the input currency and return [`BeforeSwapDelta`](/contracts/v4/reference/core/types/beforeswapdelta). The hook should `IPoolManager.mint` itself the corresponding tokens equal to the amount of the input (`amountSpecified`). It should then return a `BeforeSwapDelta` where `deltaSpecified = -amountSpecified` (the positive amount). The funds' movements are as follows: 1. User initiates a swap, specifying -100 tokenA as input 2. The hook's beforeSwap takes 100 tokenA for itself, and returns a value of 100 to PoolManager. 3. The PoolManager accounts the 100 tokens against the swap input, leaving 0 tokens remaining 4. The PoolManager does not execute swap logic, as there are no tokens left to swap 5. The PoolManager transfers the delta from the hook to the swap router, in step 2 the hook created a debt (that must be paid) 6. The swap router pays off the debt using the user's tokens ```solidity contract AsyncSwapHook is BaseHook { // ... function _beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata) internal override returns (bytes4, BeforeSwapDelta, uint24) { // AsyncSwap only works on exact-input swaps if (params.amountSpecified < 0) { // take the input token so that v3-swap is skipped... Currency input = params.zeroForOne ? key.currency0 : key.currency1; uint256 amountTaken = uint256(-params.amountSpecified); poolManager.mint(address(this), input.toId(), amountTaken); // to AsyncSwap the exact input, we return the amount that's taken by the hook return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(amountTaken.toInt128(), 0), 0); } else { return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO, 0); } } } ``` ## Testing To verify the AsyncSwap behaved properly, developers should test the swap and that token balances match expected behavior. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; // ... contract AsyncSwapTest is Test, Deployers { // ... function setUp() public { // ... } function test_asyncSwap() public { assertEq(hook.beforeSwapCount(poolId), 0); uint256 balance0Before = currency0.balanceOfSelf(); uint256 balance1Before = currency1.balanceOfSelf(); // Perform a test swap // int256 amount = -1e18; bool zeroForOne = true; BalanceDelta swapDelta = swap(poolKey, zeroForOne, amount, ZERO_BYTES); // ------------------- // uint256 balance0After = currency0.balanceOfSelf(); uint256 balance1After = currency1.balanceOfSelf(); // user paid token0 assertEq(balance0Before - balance0After, 1e18); // user did not recieve token1 (AsyncSwap) assertEq(balance1Before, balance1After); } } ``` --- ## Liquidity Hooks This guide will walk through on an example of adding and removing liquidity. There are four hook functions available to customize and extend these behavior: - `beforeAddLiquidity` - `afterAddLiquidity` - `beforeRemoveLiquidity` - `afterRemoveLiquidity` As the names suggest `beforeAddLiquidity`/`afterAddLiquidity` are functions called before or after liquidity is added to a pool. Similarly `beforeRemoveLiquidity`/`afterRemoveLiquidity` are functions called before or after liquidity is removed from a pool. This guide will go through the parameters and examples specifically for `beforeAddLiquidity` and `beforeRemoveLiquidity`. Note: The liquidity examples are not production ready code, and are implemented in a simplistic manner for the purpose of learning. ## Set Up the Contract Declare the solidity version used to compile the contract, since transient storage is used the solidity version will be `>=0.8.24`. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; ``` Import the relevant dependencies from `v4-core` and `v4-periphery`: ```solidity ``` Create a contract called LiquidityHook, use `PoolIdLibrary` to attach functions of computing ID of a pool to `PoolKey`. Declare two mappings to act as counters when calling `beforeAddLiquidity` and `beforeRemoveLiquidity`. ```solidity contract LiquidityHook is BaseHook { using PoolIdLibrary for PoolKey; // NOTE: --------------------------------------------------------- // state variables should typically be unique to a pool // a single hook contract should be able to service multiple pools // --------------------------------------------------------------- mapping(PoolId => uint256 count) public beforeAddLiquidityCount; mapping(PoolId => uint256 count) public beforeRemoveLiquidityCount; constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} ``` Override `getHookPermissions` from `BaseHook.sol` to return a struct of permissions to signal which hook functions are to be implemented. It will also be used at deployment to validate the address correctly represents the expected permissions. ```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: false, afterSwap: false, beforeDonate: false, afterDonate: false, beforeAddLiquidityReturnDelta: false, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } ``` ## beforeAddLiquidity Here the example shows that every time __before__ liquidity is added to a pool, `beforeAddLiquidityCount` for that pool will be incremented by one. ```solidity function _beforeAddLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, bytes calldata ) internal override returns (bytes4) { beforeAddLiquidityCount[key.toId()]++; return BaseHook.beforeAddLiquidity.selector; } ``` ### `beforeAddLiquidity` Parameters When triggering the `beforeAddLiquidity` hook function, there are some parameters we can make use of to customize or extend the behavior of `modifyLiquidity`. These parameters are described in `beforeAddLiquidity` from [`IHooks.sol`](https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IHooks.sol#L47). A brief overview of the parameters: - `sender` The initial `msg.sender` for the add liquidity call - `key` The key for the pool - `params` The parameters for adding liquidity i.e. `ModifyLiquidityParams` from [`IPoolManager.sol`](https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IPoolManager.sol#L125C12-L125C33) - `hookData` Arbitrary data handed into the `PoolManager` by the liquidity provider to be be passed on to the hook ## beforeRemoveLiquidity Similiar as above, every time __before__ liquidity is removed from a pool, `beforeRemoveLiquidityCount` for that pool will be incremented by one. ```solidity function _beforeRemoveLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, bytes calldata ) internal override returns (bytes4) { beforeRemoveLiquidityCount[key.toId()]++; return BaseHook.beforeRemoveLiquidity.selector; } ``` ### `beforeRemoveLiquidity` Parameters When triggering the `beforeRemoveLiquidity` hook function, there are some parameters we can make use of to customize or extend the behavior of `modifyLiquidity`. These parameters are described in `beforeRemoveLiquidity` from [`IHooks.sol`](https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IHooks.sol#L78). A brief overview of the parameters: - `sender` The initial msg.sender for the remove liquidity call - `key` The key for the pool - `params` The parameters for removing liquidity i.e. `ModifyLiquidityParams` from [`IPoolManager.sol`](https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IPoolManager.sol#L125C12-L125C33) - `hookData` Arbitrary data handed into the `PoolManager` by the liquidity provider to be be passed on to the hook ## A Complete Liquidity Hook Contract ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract LiquidityHook is BaseHook { using PoolIdLibrary for PoolKey; // NOTE: --------------------------------------------------------- // state variables should typically be unique to a pool // a single hook contract should be able to service multiple pools // --------------------------------------------------------------- mapping(PoolId => uint256 count) public beforeAddLiquidityCount; mapping(PoolId => uint256 count) public beforeRemoveLiquidityCount; constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} 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: false, afterSwap: false, beforeDonate: false, afterDonate: false, beforeAddLiquidityReturnDelta: false, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } // ----------------------------------------------- // NOTE: see IHooks.sol for function documentation // ----------------------------------------------- function _beforeAddLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, bytes calldata ) internal override returns (bytes4) { beforeAddLiquidityCount[key.toId()]++; return BaseHook.beforeAddLiquidity.selector; } function _beforeRemoveLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, bytes calldata ) internal override returns (bytes4) { beforeRemoveLiquidityCount[key.toId()]++; return BaseHook.beforeRemoveLiquidity.selector; } } ``` --- ## Set Up Local Environment Before writing the hook let's first have a local environment properly configured e.g. deploying pool manager, utility routers and test tokens. At the end of this guide a development environment will be set up to be used to build the rest of the examples in the Guides section of the docs. To get started as quickly as possible for building Uniswap v4 hooks, there is a `Quick Start` section below to clone a boilerplate and get building. To start from scratch and learn the underlying concepts, jump to the `Start from Scratch` section. ## Quick Start The Uniswap [v4-template repo](https://github.com/uniswapfoundation/v4-template) provides a basic foundry environment with required imports already pre-loaded. Click on [`Use this template`](https://github.com/new?template_name=v4-template&template_owner=uniswapfoundation) to create a new repository with it. Or simply clone it and install the dependencies: ```bash git clone https://github.com/uniswapfoundation/v4-template.git cd v4-template # requires foundry forge install forge test ``` Then hop to the [Local Node via Anvil](#local-node-via-anvil) to complete the set up and start developing. --- ## Start from Scratch In the following sections, let's walk through the steps to create the same environment set up as the boilerplate from scratch and learn the underlying concepts. ### Setting up Foundry First thing is to set up a new Foundry project. If there is no Foundry installed - follow the [Foundry Book](https://book.getfoundry.sh/getting-started/installation) for installation. Once Foundry is setup, initialize a new project: ```bash forge init counter-hook cd counter-hook ``` Then install the Uniswap `v4-core` and `v4-periphery` contracts as dependencies: ```bash forge install Uniswap/v4-core && forge install Uniswap/v4-periphery ``` Next, set up the remappings so that the shorthand syntax for importing contracts from the dependencies work nicely: ```bash forge remappings > remappings.txt ``` > If there is something wrong with the inferred remappings, please replace with the following in `remappings.txt`: ``` @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/ ``` After that, remove the default Counter contract and its associated test and script file that Foundry initially set up. To do that, either manually delete those files, or just run the following: ```bash rm ./**/Counter*.sol ``` Finally, since v4 uses transient storage which is only available after Ethereum's cancun hard fork and on Solidity versions >= 0.8.24 - some config must be set in `foundry.toml` config file. To do that, add the following lines to `foundry.toml`: ```toml # foundry.toml solc_version = "0.8.26" evm_version = "cancun" ffi = true ``` Awesome! Now it's all set to start building the hook! Let’s run a quick test to confirm everything is set up properly. ### Compile a Basic Hook Contract To confirm that the environment is configured correctly let's write a basic Counter Hook contract. Create a new file, `./src/CounterHook.sol` and paste the following code into it: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract CounterHook is BaseHook { using PoolIdLibrary for PoolKey; // NOTE: --------------------------------------------------------- // state variables should typically be unique to a pool // a single hook contract should be able to service multiple pools // --------------------------------------------------------------- mapping(PoolId => uint256 count) public beforeSwapCount; mapping(PoolId => uint256 count) public afterSwapCount; mapping(PoolId => uint256 count) public beforeAddLiquidityCount; mapping(PoolId => uint256 count) public beforeRemoveLiquidityCount; constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} 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, beforeSwapReturnDelta: false, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } // ----------------------------------------------- // NOTE: see IHooks.sol for function documentation // ----------------------------------------------- function _beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata) internal override returns (bytes4, BeforeSwapDelta, uint24) { beforeSwapCount[key.toId()]++; return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); } function _afterSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata) internal override returns (bytes4, int128) { afterSwapCount[key.toId()]++; return (BaseHook.afterSwap.selector, 0); } function _beforeAddLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, bytes calldata ) internal override returns (bytes4) { beforeAddLiquidityCount[key.toId()]++; return BaseHook.beforeAddLiquidity.selector; } function _beforeRemoveLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, bytes calldata ) internal override returns (bytes4) { beforeRemoveLiquidityCount[key.toId()]++; return BaseHook.beforeRemoveLiquidity.selector; } } ``` To compile the Counter Hook contracts in the `./src` folder, use the foundry build command: ```bash forge build ``` If the environment is compiled correctly it will display a message: ``` Compiler run successful! ``` ### Local Node via Anvil Other than writing unit tests, [Anvil](https://book.getfoundry.sh/anvil/) can be used to deploy and test hooks. With the local node up and running, use the `--rpc-url 127.0.0.1:8545` flag in tests to point the Foundry testing suite to that local node: ```bash # start anvil, a local EVM chain anvil # in a new terminal # foundry script for deploying v4 & hooks to anvil forge script script/Anvil.s.sol \ --rpc-url http://localhost:8545 \ --private-key \ --broadcast # test on the anvil local node forge test --rpc-url 127.0.0.1:8545 ``` ## Next Steps With the environment set up ready to be built on. Jump over to the guides section to learn about the Uniswap functions that can be integrated with Hook. Remember to add all contracts (.sol files) to the `./src` folder and their subsequent tests to the `./test` folder. Then test them against the local anvil node by running: ```bash forge test --rpc-url 127.0.0.1:8545 ``` ## Appendix: OpenZeppelin Hooks Library > [OpenZeppelin Hooks Library](https://docs.openzeppelin.com/uniswap-hooks/1.x/), included in [v4-template](https://github.com/uniswapfoundation/v4-template), provides secure and modular reference implementations for Uniswap v4 Hooks! If you're starting from scratch, you can install the OpenZeppelin Hooks library: ```bash $ forge install OpenZeppelin/uniswap-hooks ``` The library includes: - **BaseHook**: Core scaffolding with security checks and permission management - **BaseCustomAccounting**: For implementing hook-owned liquidity and custom token accounting - **BaseCustomCurve**: For replacing default concentrated liquidity math with custom swap logic - **BaseAsyncSwap**: For implementing non-atomic and asynchronous swaps - **BaseDynamicFee**: For implementing dynamic fee pools - **BaseOverrideFee**: For implementing dynamic fees on every swap - **BaseDynamicAfterFee**: For implementing post-swap fee adjustments based on actual swap output --- ## Swap Hooks Swaps are the most common interaction with the Uniswap protocol. When it comes to swap there are two hook functions available to customize and extend its behavior: - `beforeSwap` - `afterSwap` As the names suggest `beforeSwap`/`afterSwap` are functions called before or after a swap is executed on a pool. This guide will go through the parameters for `beforeSwap` and `afterSwap`, and a simple example of a swap hook. Note: The swap hook is not production ready code, and is implemented in a simplistic manner for the purpose of learning. ## Set Up the Contract Declare the solidity version used to compile the contract, here we will use `>=0.8.24` for the solidity version as transient storage is used. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; ``` Import the relevant dependencies from `v4-core` and `v4-periphery`: ```solidity ``` Create a contract called `SwapHook`, use `PoolIdLibrary` to attach functions of computing `poolId` for `PoolKey`. Declare two mappings as counters for `beforeSwap` and `afterSwap`. ```solidity contract SwapHook is BaseHook { using PoolIdLibrary for PoolKey; // NOTE: --------------------------------------------------------- // state variables should typically be unique to a pool // a single hook contract should be able to service multiple pools // --------------------------------------------------------------- mapping(PoolId => uint256 count) public beforeSwapCount; mapping(PoolId => uint256 count) public afterSwapCount; constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} ``` Override `getHookPermissions()` from `BaseHook` to return a struct of permissions to signal which hook functions are to be implemented. It will also be used at deployment to validate the hook address correctly represents the expected permissions. ```solidity function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, beforeAddLiquidity: false, afterAddLiquidity: false, beforeRemoveLiquidity: false, afterRemoveLiquidity: false, beforeSwap: true, afterSwap: true, beforeDonate: false, afterDonate: false, beforeSwapReturnDelta: false, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } ``` ## beforeSwap Here the example shows that every time __before__ a swap is executed in a pool, `beforeSwapCount` for that pool will be incremented by one. ```solidity function _beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata) internal override returns (bytes4, BeforeSwapDelta, uint24) { beforeSwapCount[key.toId()]++; return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); } ``` ### `beforeSwap` Parameters When triggering the `beforeSwap` hook function, there are some parameters we can make use of to customize or extend the behavior of `swap`. These parameters are described in [`beforeSwap`](/contracts/v4/reference/core/interfaces/IHooks#beforeswap) from `IHooks`. A brief overview of the parameters: - `sender` The initial `msg.sender` for the `PoolManager.swap` call - typically a swap router - `key` The key for the pool - `params` The parameters for the swap i.e. [`SwapParams`](/contracts/v4/reference/core/interfaces/IPoolManager#swapparams) from `IPoolManager` - `hookData` Arbitrary data handed into the `PoolManager` by the swapper to be passed on to the hook ## afterSwap Similiar as above, every time __after__ a swap is executed in a pool, `afterSwapCount` for that pool will be incremented by one. ```solidity function _afterSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata) internal override returns (bytes4, int128) { afterSwapCount[key.toId()]++; return (BaseHook.afterSwap.selector, 0); } ``` ### `afterSwap` Parameters When triggering the `afterSwap` hook function, there are some parameters we can make use of to customize or extend the behavior of `swap`. These parameters are described in [`afterSwap`](/contracts/v4/reference/core/interfaces/IPoolManager#swapparams) from `IHooks`. A brief overview of the parameters: - `sender` The initial `msg.sender` for the `PoolManager.swap` call - typically a swap router - `key` The key for the pool - `params` The parameters for the swap i.e. [`SwapParams`](/contracts/v4/reference/core/interfaces/IPoolManager#swapparams) from `IPoolManager` - `delta` The amount owed to the caller (positive) or owed to the pool (negative) - `hookData` Arbitrary data handed into the `PoolManager` by the swapper to be passed on to the hook ## A Complete Swap Hook Contract ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract SwapHook is BaseHook { using PoolIdLibrary for PoolKey; // NOTE: --------------------------------------------------------- // state variables should typically be unique to a pool // a single hook contract should be able to service multiple pools // --------------------------------------------------------------- mapping(PoolId => uint256 count) public beforeSwapCount; mapping(PoolId => uint256 count) public afterSwapCount; constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} 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, beforeSwapReturnDelta: false, afterSwapReturnDelta: false, afterAddLiquidityReturnDelta: false, afterRemoveLiquidityReturnDelta: false }); } // ----------------------------------------------- // NOTE: see IHooks.sol for function documentation // ----------------------------------------------- function _beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata) internal override returns (bytes4, BeforeSwapDelta, uint24) { beforeSwapCount[key.toId()]++; return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); } function _afterSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata) internal override returns (bytes4, int128) { afterSwapCount[key.toId()]++; return (BaseHook.afterSwap.selector, 0); } } ``` --- ## Batch Modify ### Context As seen in previous guides, `PositionManager` is a command-based contract. This design is conducive to batching complex liquidity operations. For example, developers can encode efficient logic to move liquidity between two positions on entirely different Pools. ### Setup See the [setup guide](./00-setup-liquidity.mdx) ## Guide Below is a general reference guide for batch-operating on multiple liquidity positions, in *solidity*. This guide does _not_ focus on a specific batch sequence, and is intended to be a general guide for `PositionManager`'s command-based interface. ### 1. Encoded Actions Actions are divided into two types: _liquidity-operations_ and _delta-resolving_. * _liquidity-operations_ - actions which that incur a *balance-change*, a change in the pool's liquidity * _delta-resolving_ - actions which facilitate token transfers, such as _settling_ and _taking_ The _ordering_ of `actions` determines the sequence of operations. The minimum number of actions is roughly two actions; and the maximum is limited by block gas limit. Additionally, _liquidity-operations_ do not have to happen prior to _delta-resolving_ actions. Developers can mix / alternate between the two types of actions. > **However** is good practice to perform _liquidity-operations_ before _delta-resolving_ actions. Minimizing token transfers and leveraging [_flash accounting_](../../concepts/02-flash-accounting.mdx) is more gas efficient Example: `Action.Y` happens after `Action.X` but before `Action.Z` ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.X), uint8(Actions.Y), uint8(Actions.Z), ...); ``` **A Note on Special Actions**: `PositionManager` supports a few _delta-resolving_ actions beyond the standard `SETTLE` and `TAKE` actions * `CLOSE_CURRENCY` - automatically determines if a currency should be settled (paid) or taken. Used for cases where callers may not know the final delta * `CLEAR_OR_TAKE`- forfeit tokens if the amount is below a specified threshold, otherwise take the tokens. Used for cases where callers may expect to produce dust * `SWEEP` - return any excess token balances to a recipient. Used for cases where callers may conversatively overpay tokens ### 2. Encoded Parameters Each action has its own parameters to encode. Generally: * _liquidity-operations_ - encode tokenIds, liquidity amounts, and slippage * _delta-resolving_ - encode currencies, amounts, and recipients Because actions are ordered, the parameters "zip" with their corresponding actions. The second parameter corresponds to the second action. Every action has its own encoded parameters ```solidity bytes[] memory params = new bytes[](3); params[0] = abi.encode(...); // parameters for the first action params[1] = abi.encode(...); // parameters for the second action params[2] = abi.encode(...); // parameters for the third action ``` ### 3. Submit Call The entrypoint for all liquidity operations is `modifyLiquidities()` ```solidity uint256 deadline = block.timestamp + 60; posm.modifyLiquidities( abi.encode(actions, params), deadline ); ``` --- ## Burn Position ### Context To liquidate a position, the _burn_ functionality can be invoked. The funds in the position will be withdrawn and all the information of the underlying token will be cleared. Burning the position is a cost effective way to exit as a liquidity provider. ### Setup See the [setup guide](./00-setup-liquidity.mdx) ## Guide Below is a step-by-step guide to burn a position. ### 1. Import and define `IPositionManager` ```solidity // inside a contract, test, or foundry script: IPositionManager posm = IPositionManager(
); ``` ### 2. Encode Actions To burn a position, two actions are required: * burn operation - clears position entirely, withdrawing funds * take pair - sends withdrawn funds to the recipient ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.BURN_POSITION), uint8(Actions.TAKE_PAIR)); ``` ### 3. Encode Parameters ```solidity bytes[] memory params = new bytes[](2); ``` The `BURN_POSITION` action requires the following parameters: | Parameter | Type | Description | |--------------|-----------|-------------------------------------------------------------------------------| | `tokenId` | _uint256_ | position identifier | | `amount0Min` | _uint128_ | the minimum amount of currency0 liquidity msg.sender is expecting to get back | | `amount1Min` | _uint128_ | the minimum amount of currency1 liquidity msg.sender is expecting to get back | | `hookData` | _bytes_ | arbitrary data that will be forwarded to hook functions | ```solidity params[0] = abi.encode(tokenId, amount0Min, amount1Min, hookData); ``` The `TAKE_PAIR` action requires the following parameters: | Parameter | Type | Description | |--------------|-----------|---------------------------------------| | `currency0` | _Currency_| first token currency | | `currency1` | _Currency_| second token currency | | `recipient` | _address_ | address that will receive the tokens | ```solidity params[1] = abi.encode(currency0, currency1, recipient); ``` ### 4. Submit Call The entrypoint for all liquidity operations is `modifyLiquidities()` ```solidity uint256 deadline = block.timestamp + 60; posm.modifyLiquidities( abi.encode(actions, params), deadline ); ``` --- ## Collect Fees ### Setup See the [setup guide](./setup-liquidity.mdx) ## Guide In order to collect fees, the integrator must execute encoded actions using the `PositionManager` contract. **Note** that there is no `COLLECT` command, instead developers must decrease liquidity with a zero liquidity change. ### 1. Import and define `IPositionManager` ```solidity // inside a contract, test, or foundry script: IPositionManager posm = IPositionManager(
); ``` ### 2. Encode actions To collect fees, the following operations are required: * decrease liquidity - collect fees from the core contract * take pair - transfer the fee revenue, as both tokens, to a recipient ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.DECREASE_LIQUIDITY), uint8(Actions.TAKE_PAIR)); ``` ### 3. Encode Parameters ```solidity bytes[] memory params = new bytes[](2); ``` The `DECREASE_LIQUIDITY` action requires the following parameters: | Parameter | Type | Description | |--------------|-----------|-------------------------------------------------------------------------------| | `tokenId` | _uint256_ | position identifier | | `liquidity` | _uint256_ | the amount of liquidity to withdraw | | `amount0Min` | _uint128_ | the minimum amount of currency0 liquidity msg.sender is expecting to get back | | `amount1Min` | _uint128_ | the minimum amount of currency1 liquidity msg.sender is expecting to get back | | `hookData` | _bytes_ | arbitrary data that will be forwarded to hook functions | **Note** that in order to collect fees we will default `liquidity`, `amount0Min` and `amount1Min` to 0. Because fee collection can not be manipulated in a front-run attack, it is safe to set the slippage values `amount0Min, amount1Min` to `0`. ```solidity /// @dev collecting fees is achieved with liquidity=0, the second parameter params[0] = abi.encode(tokenId, 0, 0, 0, hookData); ``` The `TAKE_PAIR` action requires the following parameters: * `currency0` - _Currency_, one of the tokens to be paid by msg.sender * `currency1` - _Currency_, the other token to be paid by msg.sender * `recipient` - _address_, destination of the fee revenue for both tokens ```solidity Currency currency0 = Currency.wrap(); // tokenAddress1 = 0 for native ETH Currency currency1 = Currency.wrap(); params[1] = abi.encode(currency0, currency1, recipient); ``` ### 4. Submit Call The entrypoint for all liquidity operations is `modifyLiquidities()`. ```solidity uint256 deadline = block.timestamp + 60; uint256 valueToPass = currency0.isAddressZero() ? amount0Max : 0; posm.modifyLiquidities{value: valueToPass}( abi.encode(actions, params), deadline ); ``` ## Additional notes: * To obtain the amount of fees received, callers should read token balances before and after the `.modifyLiquidities()` call. --- ## Decrease Liquidity ### Context Please note that `PositionManager` is a command-based contract, where integrators will be encoding commands and their corresponding parameters. Decreasing liquidity assumes the position already exists and the user wants to remove tokens from the position. ### Setup See the [setup guide](./00-setup-liquidity.mdx) ## Guide Below is a step-by-step guide for decreasing a position's liquidity, in *solidity*. ### 1. Import and define `IPositionManager` ```solidity // inside a contract, test, or foundry script: IPositionManager posm = IPositionManager(
); ``` ### 2. Encode Actions To decrease a position's liquidity, the first action must be: * _decrease_ operation - the subtraction of liquidity to an existing position. For _delta resolving_ operations, developers may need to choose between `TAKE_PAIR`, `CLOSE_CURRENCY`, or `CLEAR_OR_TAKE` actions. > In Uniswap v4, fee revenue is automatically debited to a position on decreasing liquidity If decreasing the liquidity requires the transfer of both tokens: * _take pair_ - receives a pair of tokens, to decrease liquidity Otherwise: * _clear or take_ - if the token amount to-be-collected is below a threshold, opt to forfeit the dust. Otherwise, claim the tokens ```solidity ``` If both tokens need to be sent: ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.DECREASE_LIQUIDITY), uint8(Actions.TAKE_PAIR)); ``` If converting fees to liquidity, forfeiting dust: ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.DECREASE_LIQUIDITY), uint8(Actions.CLEAR_OR_TAKE), uint8(Actions.CLEAR_OR_TAKE)); ``` ### 3. Encoded Parameters When taking pair: ```solidity bytes[] memory params = new bytes[](2); ``` Otherwise: ```solidity bytes[] memory params = new bytes[](3); ``` The `DECREASE_LIQUIDITY` action requires the following parameters: | Parameter | Type | Description | |--------------|-----------|-------------------------------------------------------------------------------| | `tokenId` | _uint256_ | position identifier | | `liquidity` | _uint256_ | the amount of liquidity to remove | | `amount0Min` | _uint128_ | the minimum amount of currency0 liquidity msg.sender is willing to receive | | `amount1Min` | _uint128_ | the minimum amount of currency1 liquidity msg.sender is willing to receive | | `hookData` | _bytes_ | arbitrary data that will be forwarded to hook functions | ```solidity params[0] = abi.encode(tokenId, liquidity, amount0Min, amount1Min, hookData); ``` The `TAKE_PAIR` action requires the following parameters: * `currency0` - _Currency_, one of the tokens to be received * `currency1` - _Currency_, the other token to be received * `recipient` - _Recipient_, the recipient to receive the tokens In the above case, the parameter encoding is: ```solidity Currency currency0 = Currency.wrap(); // tokenAddress1 = 0 for native ETH Currency currency1 = Currency.wrap(); params[1] = abi.encode(currency0, currency1, recipient); ``` The `CLEAR_OR_TAKE` action requires one `currency` and: * `amountMax` - _uint256_, the maximum threshold to concede dust, otherwise taking the dust. In this case, the parameter encoding is: ```solidity params[1] = abi.encode(currency0, amount0Max); params[2] = abi.encode(currency1, amount1Max); ``` ### 4. Submit Call The entrypoint for all liquidity operations is `modifyLiquidities()`. ```solidity uint256 deadline = block.timestamp + 60; uint256 valueToPass = currency0.isAddressZero() ? amount0Max : 0; posm.modifyLiquidities{value: valueToPass}( abi.encode(actions, params), deadline ); ``` --- ## Increase Liquidity ### Context Please note that `PositionManager` is a command-based contract, where integrators will be encoding commands and their corresponding parameters. Increasing liquidity assumes the position already exists and the user wants to add more tokens to the position. ### Setup See the [setup guide](./00-setup-liquidity.mdx) ## Guide Below is a step-by-step guide for increasing a position's liquidity, in *solidity*. ### 1. Import and define `IPositionManager` ```solidity // inside a contract, test, or foundry script: IPositionManager posm = IPositionManager(
); ``` ### 2. Encode Actions To increase a position's liquidity, the first action must be: * _increase_ operation - the addition of liquidity to an existing position. For _delta resolving_ operations, developers may need to choose between `SETTLE_PAIR`, `CLOSE_CURRENCY`, or `CLEAR_OR_TAKE` actions. > In Uniswap v4, fee revenue is automatically credited to a position on increasing liquidity > There are some cases, where the fee revenue can entirely "pay" for a liquidity increase, and remainder tokens need to be collected If increasing the liquidity requires the transfer of both tokens: * _settle pair_ - pays a pair of tokens, to increase liquidity **If increasing the liquidity for ETH positions, a third action is required:** * _sweep_ - to recover excess eth sent to the position manager Otherwise: * _close currency_ - automatically determines if a currency should be settled or taken. * OR _clear or take_ - if the token amount to-be-collected is below a threshold, opt to forfeit the dust. Otherwise, claim the tokens ```solidity ``` If both tokens need to be sent: ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.INCREASE_LIQUIDITY), uint8(Actions.SETTLE_PAIR)); ``` If increasing liquidity for ETH positions: ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.INCREASE_LIQUIDITY), uint8(Actions.SETTLE_PAIR), uint8(Actions.SWEEP)); ``` If converting fees to liquidity, and expect excess fees to be collected ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.INCREASE_LIQUIDITY), uint8(Actions.CLOSE_CURRENCY), uint8(Actions.CLOSE_CURRENCY)); ``` If converting fees to liquidity, forfeiting dust: ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.INCREASE_LIQUIDITY), uint8(Actions.CLEAR_OR_TAKE), uint8(Actions.CLEAR_OR_TAKE)); ``` ### 3. Encoded Parameters When settling pair (for non-ETH positions): ```solidity bytes[] memory params = new bytes[](2); ``` Otherwise: ```solidity bytes[] memory params = new bytes[](3); ``` The `INCREASE_LIQUIDITY` action requires the following parameters: | Parameter | Type | Description | |--------------|-----------|-------------------------------------------------------------------------------| | `tokenId` | _uint256_ | position identifier | | `liquidity` | _uint256_ | the amount of liquidity to add | | `amount0Max` | _uint128_ | the maximum amount of currency0 liquidity msg.sender is willing to pay | | `amount1Max` | _uint128_ | the maximum amount of currency1 liquidity msg.sender is willing to pay | | `hookData` | _bytes_ | arbitrary data that will be forwarded to hook functions | ```solidity params[0] = abi.encode(tokenId, liquidity, amount0Max, amount1Max, hookData); ``` The `SETTLE_PAIR` action requires the following parameters: * `currency0` - _Currency_, one of the tokens to be paid by msg.sender * `currency1` - _Currency_, the other token to be paid by msg.sender In the above case, the parameter encoding is: ```solidity Currency currency0 = Currency.wrap(); // tokenAddress1 = 0 for native ETH Currency currency1 = Currency.wrap(); params[1] = abi.encode(currency0, currency1); ``` The `SWEEP` action requires the following parameters: * `currency` - _Currency_, token to sweep - most commonly native Ether: `CurrencyLibrary.ADDRESS_ZERO` * `recipient` - _address_, where to send excess tokens In this case, the parameter encoding is: ```solidity params[2] = abi.encode(currency, recipient); ``` The `CLOSE_CURRENCY` action requires only one `currency` parameter and the encoding is: ```solidity params[1] = abi.encode(currency0) params[2] = abi.encode(currency1) ``` The `CLEAR_OR_TAKE` action requires one `currency` and: * `amountMax` - _uint256_, the maximum threshold to concede dust, otherwise taking the dust. In this case, the parameter encoding is: ```solidity params[1] = abi.encode(currency0, amount0Max); params[2] = abi.encode(currency1, amount1Max); ``` ### 4. Submit Call The entrypoint for all liquidity operations is `modifyLiquidities()`. ```solidity uint256 deadline = block.timestamp + 60; uint256 valueToPass = currency0.isAddressZero() ? amount0Max : 0; posm.modifyLiquidities{value: valueToPass}( abi.encode(actions, params), deadline ); ``` --- ## Mint Position Similar to Uniswap v3, liquidity positions are minted as ERC-721 tokens and depend on a *periphery* contract. v4's `PositionManager` contract will facilitate liquidity management ## Context Please note that `PositionManager` is a command-based contract, where integrators will be encoding commands and their corresponding parameters. ## Setup See the [setup guide](./00-setup-liquidity.mdx) ## Guide Below is a step-by-step guide for minting a v4 liquidity position, in *solidity* ### 1. Import and define `IPositionManager` ```solidity // inside a contract, test, or foundry script: IPositionManager posm = IPositionManager(
); ``` ### 2. Encode Actions To mint a position, two actions are required: * mint operation - the creation of the liquidity position * settle pair - the two tokens to be paid by msg.sender **If providing ETH liquidity, a third action is required:** * sweep - to recover excess eth sent to the position manager ```solidity bytes memory actions = abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR)); // For ETH liquidity positions bytes memory actions = abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR), uint8(Actions.SWEEP)); ``` ### 3. Encode Parameters ```solidity bytes[] memory params = new bytes[](2); // new bytes[](3) for ETH liquidity positions ``` The `MINT_POSITION` action requires the following parameters: | Parameter | Type | Description | |--------------|-----------|----------------------------------------------------------------| | `poolKey` | _PoolKey_ | where the liquidity will be added to | | `tickLower` | _int24_ | the lower tick boundary of the position | | `tickUpper` | _int24_ | the upper tick boundary of the position | | `liquidity` | _uint256_ | the amount of liquidity units to mint | | `amount0Max` | _uint128_ | the maximum amount of currency0 msg.sender is willing to pay | | `amount1Max` | _uint128_ | the maximum amount of currency1 msg.sender is willing to pay | | `recipient` | _address_ | the address that will receive the liquidity position (ERC-721) | | `hookData` | _bytes_ | arbitrary data that will be forwarded to hook functions | ```solidity Currency currency0 = Currency.wrap(); // tokenAddress1 = 0 for native ETH Currency currency1 = Currency.wrap(); PoolKey poolKey = PoolKey(currency0, currency1, 3000, 60, IHooks(hook)); params[0] = abi.encode(poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, recipient, hookData); ``` The `SETTLE_PAIR` action requires the following parameters: * `currency0` - _Currency_, one of the tokens to be paid by msg.sender * `currency1` - _Currency_, the other token to be paid by msg.sender ```solidity params[1] = abi.encode(currency0, currency1); ``` The `SWEEP` action requires the following parameters: * `currency` - _Currency_, token to sweep - most commonly native Ether: `CurrencyLibrary.ADDRESS_ZERO` * `recipient` - _address_, where to send excess tokens ```solidity params[2] = abi.encode(currency, recipient); ``` ### 4. Submit Call The entrypoint for all liquidity operations is `modifyLiquidities()` ```solidity uint256 deadline = block.timestamp + 60; uint256 valueToPass = currency0.isAddressZero() ? amount0Max : 0; posm.modifyLiquidities{value: valueToPass}( abi.encode(actions, params), deadline ); ``` ## Additional notes: * To obtain balance changes, callers should read token balances before and after the `.modifyLiquidities()` call --- ## Setup(Manage-liquidity) For users looking to interact with the canonical Uniswap v4 `PositionManager`, _v4-periphery_ is a required dependency Currently, developing with Uniswap v4 _requires [foundry](https://book.getfoundry.sh)_ ## Quickstart _Use [v4-template](https://github.com/new?template_name=v4-template&template_owner=uniswapfoundation)_, which has pre-configured dependencies and tests for Uniswap v4 Clone the repository made from _v4-template_ ```bash git clone https://github.com// ``` Install dependencies ```bash forge install ``` --- ## 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-periphery/=lib/v4-periphery/ ``` --- ## Subscriber(Quickstart) ### Context For developers looking to support custom _liquidity mining_, Subscriber contracts can be used to receive notifications about position modifications or transfers. ## Guide ### 1. Implement the [`ISubscriber`](https://github.com/Uniswap/v4-periphery/blob/main/src/interfaces/ISubscriber.sol) interface Can also refer to [MockSubscriber](https://github.com/Uniswap/v4-periphery/blob/main/test/mocks/MockSubscriber.sol) for an actual implementation example. ```solidity contract MySubscriber is ISubscriber { uint256 public notifySubscribeCount; uint256 public notifyUnsubscribeCount; uint256 public notifyModifyLiquidityCount; uint256 public notifyBurnCount; // other implementations... function notifySubscribe(uint256, bytes memory) external onlyByPosm { notifySubscribeCount++; } function notifyUnsubscribe(uint256) external onlyByPosm { notifyUnsubscribeCount++; } function notifyModifyLiquidity(uint256, int256, BalanceDelta) external onlyByPosm { notifyModifyLiquidityCount++; } function notifyBurn(uint256, address, PositionInfo, uint256, BalanceDelta) external onlyByPosm { notifyBurnCount++; } } ``` ### 2. A caveat on `unsubscribe()` To prevent gas griefing during unsubscription, Uniswap v4 sets a fixed variable [`unsubscribeGasLimit`](/contracts/v4/reference/periphery/interfaces/INotifier#unsubscribegaslimit) when calling a subscriber’s `notifyUnsubscribe()` function. Without this limit, malicious subscribers could prevent liquidity providers from unsubscribing. If `notifyUnsubscribe()` were to consume too much gas, it would cause the unsubscription transaction to revert, thus leading to a denial-of-service With the gas limit in place, if the subscriber’s notification fails, the unsubscription will still succeed and only the notification to the subscriber is skipped. From [`_unsubscribe()`](https://github.com/Uniswap/v4-periphery/blob/main/src/base/Notifier.sol#L80) on `Notifier`: ```solidity if (address(_subscriber).code.length > 0) { // require that the remaining gas is sufficient to notify the subscriber // otherwise, users can select a gas limit where .notifyUnsubscribe hits OutOfGas yet the // transaction/unsubscription can still succee if (gasleft() < unsubscribeGasLimit) GasLimitTooLow.selector.revertWith(); try _subscriber.notifyUnsubscribe{gas: unsubscribeGasLimit}(tokenId) {} catch {} } ``` ### 3. Opt-in to a subscriber contract To opt-in to a subscriber contract, call [`subscribe()`](/contracts/v4/reference/periphery/interfaces/INotifier#subscribe) on `PositionManager`. ```solidity IPositionManager posm = IPositionManager(
); ISubscriber mySubscriber = ISubscriber(
); bytes memory optionalData = ...; posm.subscribe(tokenId, mySubscriber, optionalData); ``` --- ## Swap(Quickstart) ## Swapping on Uniswap v4 The `Universal Router` is a flexible, gas-efficient contract designed to execute complex swap operations across various protocols, including Uniswap v4. It serves as an intermediary between users and the Uniswap v4 `PoolManager`, handling the intricacies of swap execution. Although it's technically possible to interact directly with the PoolManager contract for swaps, this approach is not recommended due to its complexity and potential inefficiencies. Instead, the Universal Router is the preferred method, as it abstracts away these complexities. By using the Universal Router, developers and users can ensure a more straightforward, efficient, and standardized approach to executing swaps on v4 pools, aligning with best practices for Uniswap interactions. ## Configuring Universal Router for Uniswap v4 Swaps Set up a foundry project and install the necessary dependencies: ```bash forge install uniswap/v4-core forge install uniswap/v4-periphery forge install uniswap/permit2 forge install uniswap/universal-router forge install uniswap/v3-core forge install uniswap/v2-core forge install OpenZeppelin/openzeppelin-contracts ``` In the `remappings.txt`, add the following: ``` @uniswap/v4-core/=lib/v4-core/ @uniswap/v4-periphery/=lib/v4-periphery/ @uniswap/permit2/=lib/permit2/ @uniswap/universal-router/=lib/universal-router/ @uniswap/v3-core/=lib/v3-core/ @uniswap/v2-core/=lib/v2-core/ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ [...] ``` ### Step 1: Set Up the Project First, we need to set up our project and import the necessary dependencies. We'll create a new Solidity contract for our example. ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.26; contract Example { using StateLibrary for IPoolManager; UniversalRouter public immutable router; IPoolManager public immutable poolManager; IPermit2 public immutable permit2; constructor(address _router, address _poolManager, address _permit2) { router = UniversalRouter(payable(_router)); poolManager = IPoolManager(_poolManager); permit2 = IPermit2(_permit2); } // We'll add more functions here } ``` In this step, we're importing the necessary contracts and interfaces: - `UniversalRouter`: This will be our main interface for executing swaps. It provides a flexible way to interact with various Uniswap versions and other protocols. - `Commands`: This library contains the command definitions used by the UniversalRouter. - `IPoolManager`: This interface is needed for interacting with Uniswap v4 pools. While we don't directly use it in our simple example, it's often necessary for more complex interactions with v4 pools. - `IPermit2`: This interface allows us to interact with the Permit2 contract, which provides enhanced token approval functionality. - `StateLibrary`: This provides optimized functions for interacting with the PoolManager's state. By using `StateLibrary`, we can more efficiently read and manipulate pool states, which is crucial for many operations in Uniswap v4. ### Step 2: Implement Token Approval with Permit2 `UniversalRouter` integrates with [Permit2](https://github.com/Uniswap/permit2), to enable users to have more safety, flexibility, and control over their ERC20 token approvals. Before we can execute swaps, we need to ensure our contract can transfer tokens. We’ll implement a function to approve the Universal Router to spend tokens on behalf of our contract. Here, for testing purposes, we set up our contract to use Permit2 with the UniversalRouter: ```solidity function approveTokenWithPermit2( address token, uint160 amount, uint48 expiration ) external { IERC20(token).approve(address(permit2), type(uint256).max); permit2.approve(token, address(router), amount, expiration); } ``` This function first approves Permit2 to spend the token, then uses Permit2 to approve the UniversalRouter with a specific amount and expiration time. ### Step 3: Implementing a Swap Function #### 3.1: Function Signature First, let’s define our function signature: ```solidity function swapExactInputSingle( PoolKey calldata key, // PoolKey struct that identifies the v4 pool uint128 amountIn, // Exact amount of tokens to swap uint128 minAmountOut, // Minimum amount of output tokens expected uint256 deadline // Timestamp after which the transaction will revert ) external returns (uint256 amountOut) { // Implementation will follow } ``` **Important note:** 1. The deadline parameter allows users to specify when their transaction should expire. This protects against unfavorable execution due to network delays or MEV attacks. 2. When swapping tokens involving native ETH, we use `Currency.wrap(address(0))` to represent ETH in the `PoolKey` struct. ```solidity struct PoolKey { /// @notice The lower currency of the pool, sorted numerically. /// For native ETH, Currency currency0 = Currency.wrap(address(0)); Currency currency0; /// @notice The higher currency of the pool, sorted numerically Currency currency1; /// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1, the pool has a dynamic fee and must be exactly equal to 0x800000 uint24 fee; /// @notice Ticks that involve positions must be a multiple of tick spacing int24 tickSpacing; /// @notice The hooks of the pool IHooks hooks; } ``` #### 3.2: Encoding the Swap Command When encoding a swap command for the Universal Router, we need to choose between two types of swaps: 1. Exact Input Swaps: Use this swap-type when you know the exact amount of tokens you want to swap in, and you're willing to accept any amount of output tokens above your minimum. This is common when you want to sell a specific amount of tokens. 2. Exact Output Swaps: Use this swap-type when you need a specific amount of output tokens, and you're willing to spend up to a maximum amount of input tokens. This is useful when you need to acquire a precise amount of tokens, for example, to repay a loan or meet a specific requirement. Next, we encode the swap command: ```solidity bytes memory commands = abi.encodePacked(uint8(Commands.V4_SWAP)); ``` Here, we're using `V4_SWAP`, which tells the Universal Router that we want to perform a swap on a Uniswap v4 pool. The specific type of swap (exact input or exact output) will be determined by the V4Router actions we encode later. As we saw earlier, we encode this as a single byte, which is how the Universal Router expects to receive commands. Check the complete list of [commands](https://docs.uniswap.org/contracts/universal-router/technical-reference#command). #### 3.3: Action Encoding Now, let’s encode the actions for the swap: ```solidity // Encode V4Router actions bytes memory actions = abi.encodePacked( uint8(Actions.SWAP_EXACT_IN_SINGLE), uint8(Actions.SETTLE_ALL), uint8(Actions.TAKE_ALL) ); ``` These actions define the sequence of operations that will be performed in our v4 swap: 1. `SWAP_EXACT_IN_SINGLE`: This action specifies that we want to perform an exact input swap using a single pool. 2. `SETTLE_ALL`: This action ensures all input tokens involved in the swap are properly paid. This is part of v4's settlement pattern for handling token transfers. 3. `TAKE_ALL`: This final action collects all output tokens after the swap is complete. The sequence of these actions is important as they define the complete flow of our swap operation from start to finish. #### 3.4: Preparing the Swap Inputs For our v4 swap, we need to prepare three parameters that correspond to our encoded actions: ```solidity bytes[] memory params = new bytes[](3); // First parameter: swap configuration params[0] = abi.encode( IV4Router.ExactInputSingleParams({ poolKey: key, zeroForOne: true, // true if we're swapping token0 for token1 amountIn: amountIn, // amount of tokens we're swapping amountOutMinimum: minAmountOut, // minimum amount we expect to receive hookData: bytes("") // no hook data needed }) ); // Second parameter: specify input tokens for the swap // encode SETTLE_ALL parameters params[1] = abi.encode(key.currency0, amountIn); // Third parameter: specify output tokens from the swap params[2] = abi.encode(key.currency1, minAmountOut); ``` Each encoded parameter corresponds to a specific action in our swap: 1. The first parameter configures how the swap should be executed, defining the pool, amounts, and other swap-specific details 2. The second parameter defines what tokens we're putting into the swap 3. The third parameter defines what tokens we expect to receive from the swap The sequence of these parameters must match the sequence of actions we defined earlier (`SWAP_EXACT_IN_SINGLE`, `SETTLE_ALL`, and `TAKE_ALL`). #### 3.5: Executing the Swap Now we can execute the swap using the Universal Router: ```solidity bytes[] memory inputs = new bytes[](1); // Combine actions and params into inputs inputs[0] = abi.encode(actions, params); // Execute the swap uint256 deadline = block.timestamp + 20; router.execute(commands, inputs, deadline); ``` This prepares and executes the swap based on our encoded commands, actions, and parameters. > **Note**: Never use block.timestamp or type(uint256).max as the deadline parameter. > #### 3.6: (Optional) Verifying the Swap Output After the swap, we need to verify that we received at least the minimum amount of tokens we specified: ```solidity amountOut = key.currency1.balanceOf(address(this)); require(amountOut >= minAmountOut, "Insufficient output amount"); ``` #### 3.7: Returning the Result Finally, we return the amount of tokens we received: ```solidity return amountOut; ``` This allows the caller of the function to know exactly how many tokens were received in the swap. Here's the complete swap function that combines all the steps we've covered: ```solidity function swapExactInputSingle( PoolKey calldata key, uint128 amountIn, uint128 minAmountOut ) external returns (uint256 amountOut) { // Encode the Universal Router command bytes memory commands = abi.encodePacked(uint8(Commands.V4_SWAP)); bytes[] memory inputs = new bytes[](1); // Encode V4Router actions bytes memory actions = abi.encodePacked( uint8(Actions.SWAP_EXACT_IN_SINGLE), uint8(Actions.SETTLE_ALL), uint8(Actions.TAKE_ALL) ); // Prepare parameters for each action bytes[] memory params = new bytes[](3); params[0] = abi.encode( IV4Router.ExactInputSingleParams({ poolKey: key, zeroForOne: true, amountIn: amountIn, amountOutMinimum: minAmountOut, hookData: bytes("") }) ); params[1] = abi.encode(key.currency0, amountIn); params[2] = abi.encode(key.currency1, minAmountOut); // Combine actions and params into inputs inputs[0] = abi.encode(actions, params); // Execute the swap uint256 deadline = block.timestamp + 20; router.execute(commands, inputs, deadline); // Verify and return the output amount amountOut = key.currency1.balanceOf(address(this)); require(amountOut >= minAmountOut, "Insufficient output amount"); return amountOut; } ``` --- ## ERC6909 [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/ERC6909.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IERC6909Claims](contracts/v4/reference/core/interfaces/IERC6909Claims.md) **Author:** Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC6909.sol) Minimalist and gas efficient standard ERC6909 implementation. *Copied from the commit at 4b47a19038b798b4a33d9749d25e570443520647* *This contract has been modified from the implementation at the above link.* ## State Variables ### isOperator ```solidity mapping(address owner => mapping(address operator => bool isOperator)) public isOperator; ``` ### balanceOf ```solidity mapping(address owner => mapping(uint256 id => uint256 balance)) public balanceOf; ``` ### allowance ```solidity mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) public allowance; ``` ## Functions ### transfer ```solidity function transfer(address receiver, uint256 id, uint256 amount) public virtual returns (bool); ``` ### transferFrom ```solidity function transferFrom(address sender, address receiver, uint256 id, uint256 amount) public virtual returns (bool); ``` ### approve ```solidity function approve(address spender, uint256 id, uint256 amount) public virtual returns (bool); ``` ### setOperator ```solidity function setOperator(address operator, bool approved) public virtual returns (bool); ``` ### supportsInterface ```solidity function supportsInterface(bytes4 interfaceId) public view virtual returns (bool); ``` ### _mint ```solidity function _mint(address receiver, uint256 id, uint256 amount) internal virtual; ``` ### _burn ```solidity function _burn(address sender, uint256 id, uint256 amount) internal virtual; ``` --- ## ERC6909Claims [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/ERC6909Claims.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [ERC6909](contracts/v4/reference/core/ERC6909.md) ERC6909Claims inherits ERC6909 and implements an internal burnFrom function ## Functions ### _burnFrom Burn `amount` tokens of token type `id` from `from`. *if sender is not `from` they must be an operator or have sufficient allowance.* ```solidity function _burnFrom(address from, uint256 id, uint256 amount) internal; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`from`|`address`|The address to burn tokens from.| |`id`|`uint256`|The currency to burn.| |`amount`|`uint256`|The amount to burn.| --- ## Extsload [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/Extsload.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IExtsload](contracts/v4/reference/core/interfaces/IExtsload.md) Enables public storage access for efficient state retrieval by external contracts. https://eips.ethereum.org/EIPS/eip-2330#rationale ## Functions ### extsload Called by external contracts to access granular pool state ```solidity function extsload(bytes32 slot) external view returns (bytes32); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`slot`|`bytes32`|Key of slot to sload| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes32`|value The value of the slot as bytes32| ### extsload Called by external contracts to access granular pool state ```solidity function extsload(bytes32 startSlot, uint256 nSlots) external view returns (bytes32[] memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`startSlot`|`bytes32`|| |`nSlots`|`uint256`|| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes32[]`|value The value of the slot as bytes32| ### extsload Called by external contracts to access granular pool state ```solidity function extsload(bytes32[] calldata slots) external view returns (bytes32[] memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`slots`|`bytes32[]`|| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes32[]`|value The value of the slot as bytes32| --- ## Exttload [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/Exttload.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IExttload](contracts/v4/reference/core/interfaces/IExttload.md) Enables public transient storage access for efficient state retrieval by external contracts. https://eips.ethereum.org/EIPS/eip-2330#rationale ## Functions ### exttload Called by external contracts to access transient storage of the contract ```solidity function exttload(bytes32 slot) external view returns (bytes32); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`slot`|`bytes32`|Key of slot to tload| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes32`|value The value of the slot as bytes32| ### exttload Called by external contracts to access transient storage of the contract ```solidity function exttload(bytes32[] calldata slots) external view returns (bytes32[] memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`slots`|`bytes32[]`|| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes32[]`|value The value of the slot as bytes32| --- ## IPoolManager The `IPoolManager` interface defines the main methods for interacting with the Uniswap V4 pool manager contract. It exposes the core _swap lifecycle_ operations ## ModifyLiquidityParams Structure used to modify liquidity in a pool. - `tickLower`: Lower tick boundary of the position - `tickUpper`: Upper tick boundary of the position - `liquidityDelta`: Amount of liquidity to add (positive) or remove (negative) - `salt`: A value to set if you want unique liquidity positions at the same range Used in the `modifyLiquidity` function to add or remove liquidity from a specific position in the pool. ## SwapParams Structure used to execute a swap in a pool. - `zeroForOne`: Direction of the swap (true for token0 to token1, false for token1 to token0) - `amountSpecified`: The desired input amount if negative (exactIn), or the desired output amount if positive (exactOut) - `sqrtPriceLimitX96`: Slippage limit represented as [Q64X96](https://uniswapv3book.com/milestone_3/more-on-fixed-point-numbers.html#:~:text=The%20Q64.,and%2018%20signify%20decimal%20places.) notation Used in the `swap` function to define the behavior of our swap. ## Methods ### initialize ```solidity function initialize(PoolKey memory key, uint160 sqrtPriceX96) external returns (int24 tick); ``` Initialize a new pool by defining its parameters: token pair, fee tier, tick spacing, hook contract, and starting price | Param Name | Type | Description | |---------------|-----------|--------------------------------------------------| | key | PoolKey | The key defining the pool to initialize | | sqrtPriceX96 | uint160 | The initial sqrt price of the pool as a Q64.96 value | Returns the initial tick value of the pool. ### unlock ```solidity function unlock(bytes calldata data) external returns (bytes memory); ``` Provides a single entry point for all pool operations. The provided data is passed to the callback for execution. | Param Name | Type | Description | |------------|-------|--------------------------------------------------------------------------------------| | data | bytes | Any data to pass to the callback via `IUnlockCallback(msg.sender).unlockCallback(data)` | Returns the data returned by the callback. ### modifyLiquidity ```solidity function modifyLiquidity( PoolKey memory key, ModifyLiquidityParams memory params, bytes calldata hookData ) external returns (BalanceDelta, BalanceDelta); ``` Modifies the liquidity for the given pool. Can be used to add or remove liquidity, or collect fees > passing zero will collect fees for the given tick range | Param Name | Type | Description | |------------|------------------------|--------------------------------------------------| | key | PoolKey | The key of the pool to modify liquidity in | | params | ModifyLiquidityParams | The parameters for modifying the liquidity position | | hookData | bytes | Any data to pass to a hook contract on the before/add liquidity hooks | Returns the balance delta for the caller (total of principal and fees) and the fee delta generated in the liquidity range. ### swap ```solidity function swap(PoolKey memory key, SwapParams memory params, bytes calldata hookData) external returns (BalanceDelta); ``` Executes a swap against the given pool using the provided parameters. | Param Name | Type | Description | |------------|------------|-----------------------------------------| | key | PoolKey | The key of the pool to swap in | | params | SwapParams | The parameters for executing the swap | | hookData | bytes | Any data to pass to a hook contract on the before/afterSwap hooks | Returns the balance delta for the address initiating the swap. Swapping on low liquidity pools may cause unexpected swap amounts when liquidity available is less than amountSpecified. Additionally note that if interacting with hooks that have the BEFORE_SWAP_RETURNS_DELTA_FLAG or AFTER_SWAP_RETURNS_DELTA_FLAG, the hook may alter the swap input/output. Integrators should perform checks on the returned swapDelta. ### donate ```solidity function donate(PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData) external returns (BalanceDelta); ``` Donates the specified currency amounts to the pool. | Param Name | Type | Description | |------------|----------|-------------------------------------| | key | PoolKey | The key of the pool to donate to | | amount0 | uint256 | The amount of token0 to donate | | amount1 | uint256 | The amount of token1 to donate | | hookData | bytes | Any data to pass to a hook contract on the before/afterDonate hooks| Returns the balance delta representing the donated amounts. --- ## NoDelegateCall [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/NoDelegateCall.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Base contract that provides a modifier for preventing delegatecall to methods in a child contract ## State Variables ### original *The original address of this contract* ```solidity address private immutable original; ``` ## Functions ### constructor ```solidity constructor(); ``` ### checkNotDelegateCall *Private method is used instead of inlining into modifier because modifiers are copied into each method, and the use of immutable means the address bytes are copied in every place the modifier is used.* ```solidity function checkNotDelegateCall() private view; ``` ### noDelegateCall Prevents delegatecall into the modified method ```solidity modifier noDelegateCall(); ``` ## Errors ### DelegateCallNotAllowed ```solidity error DelegateCallNotAllowed(); ``` --- ## PoolManager(Core) [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/PoolManager.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IPoolManager](contracts/v4/reference/core/interfaces/IPoolManager.md), [ProtocolFees](contracts/v4/reference/core/ProtocolFees.md), [NoDelegateCall](contracts/v4/reference/core/NoDelegateCall.md), [ERC6909Claims](contracts/v4/reference/core/ERC6909Claims.md), [Extsload](contracts/v4/reference/core/Extsload.md), [Exttload](contracts/v4/reference/core/Exttload.md) Holds the state for all pools ## State Variables ### MAX_TICK_SPACING ```solidity int24 private constant MAX_TICK_SPACING = TickMath.MAX_TICK_SPACING; ``` ### MIN_TICK_SPACING ```solidity int24 private constant MIN_TICK_SPACING = TickMath.MIN_TICK_SPACING; ``` ### _pools ```solidity mapping(PoolId id => Pool.State) internal _pools; ``` ## Functions ### onlyWhenUnlocked This will revert if the contract is locked ```solidity modifier onlyWhenUnlocked(); ``` ### constructor ```solidity constructor(address initialOwner) ProtocolFees(initialOwner); ``` ### unlock All interactions on the contract that account deltas require unlocking. A caller that calls `unlock` must implement `IUnlockCallback(msg.sender).unlockCallback(data)`, where they interact with the remaining functions on this contract. *The only functions callable without an unlocking are `initialize` and `updateDynamicLPFee`* ```solidity function unlock(bytes calldata data) external override returns (bytes memory result); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`data`|`bytes`|Any data to pass to the callback, via `IUnlockCallback(msg.sender).unlockCallback(data)`| **Returns** |Name|Type|Description| |----|----|-----------| |`result`|`bytes`|The data returned by the call to `IUnlockCallback(msg.sender).unlockCallback(data)`| ### initialize Initialize the state for a given pool ID *A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee* ```solidity function initialize(PoolKey memory key, uint160 sqrtPriceX96) external noDelegateCall returns (int24 tick); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool key for the pool to initialize| |`sqrtPriceX96`|`uint160`|The initial square root price| **Returns** |Name|Type|Description| |----|----|-----------| |`tick`|`int24`|The initial tick of the pool| ### modifyLiquidity Modify the liquidity for the given pool *Poke by calling with a zero liquidityDelta* ```solidity function modifyLiquidity(PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, bytes calldata hookData) external onlyWhenUnlocked noDelegateCall returns (BalanceDelta callerDelta, BalanceDelta feesAccrued); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool to modify liquidity in| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for modifying the liquidity| |`hookData`|`bytes`|The data to pass through to the add/removeLiquidity hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`callerDelta`|`BalanceDelta`|The balance delta of the caller of modifyLiquidity. This is the total of both principal, fee deltas, and hook deltas if applicable| |`feesAccrued`|`BalanceDelta`|The balance delta of the fees generated in the liquidity range. Returned for informational purposes| ### swap Swap against the given pool *Swapping on low liquidity pools may cause unexpected swap amounts when liquidity available is less than amountSpecified. Additionally note that if interacting with hooks that have the BEFORE_SWAP_RETURNS_DELTA_FLAG or AFTER_SWAP_RETURNS_DELTA_FLAG the hook may alter the swap input/output. Integrators should perform checks on the returned swapDelta.* ```solidity function swap(PoolKey memory key, IPoolManager.SwapParams memory params, bytes calldata hookData) external onlyWhenUnlocked noDelegateCall returns (BalanceDelta swapDelta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool to swap in| |`params`|`IPoolManager.SwapParams`|The parameters for swapping| |`hookData`|`bytes`|The data to pass through to the swap hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`swapDelta`|`BalanceDelta`|The balance delta of the address swapping| ### _swap Internal swap function to execute a swap, take protocol fees on input token, and emit the swap event ```solidity function _swap(Pool.State storage pool, PoolId id, Pool.SwapParams memory params, Currency inputCurrency) internal returns (BalanceDelta); ``` ### donate Donate the given currency amounts to the in-range liquidity providers of a pool *Calls to donate can be frontrun adding just-in-time liquidity, with the aim of receiving a portion donated funds. Donors should keep this in mind when designing donation mechanisms.* ```solidity function donate(PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData) external onlyWhenUnlocked noDelegateCall returns (BalanceDelta delta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The key of the pool to donate to| |`amount0`|`uint256`|The amount of currency0 to donate| |`amount1`|`uint256`|The amount of currency1 to donate| |`hookData`|`bytes`|The data to pass through to the donate hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`delta`|`BalanceDelta`|BalanceDelta The delta of the caller after the donate| ### sync Writes the current ERC20 balance of the specified currency to transient storage This is used to checkpoint balances for the manager and derive deltas for the caller. *This MUST be called before any ERC20 tokens are sent into the contract, but can be skipped for native tokens because the amount to settle is determined by the sent value. However, if an ERC20 token has been synced and not settled, and the caller instead wants to settle native funds, this function can be called with the native currency to then be able to settle the native currency* ```solidity function sync(Currency currency) external; ``` ### take Called by the user to net out some value owed to the user *Will revert if the requested amount is not available, consider using `mint` instead* ```solidity function take(Currency currency, address to, uint256 amount) external onlyWhenUnlocked; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`Currency`|The currency to withdraw from the pool manager| |`to`|`address`|The address to withdraw to| |`amount`|`uint256`|The amount of currency to withdraw| ### settle Called by the user to pay what is owed ```solidity function settle() external payable onlyWhenUnlocked returns (uint256); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|paid The amount of currency settled| ### settleFor Called by the user to pay on behalf of another address ```solidity function settleFor(address recipient) external payable onlyWhenUnlocked returns (uint256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`recipient`|`address`|The address to credit for the payment| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|paid The amount of currency settled| ### clear WARNING - Any currency that is cleared, will be non-retrievable, and locked in the contract permanently. A call to clear will zero out a positive balance WITHOUT a corresponding transfer. *This could be used to clear a balance that is considered dust. Additionally, the amount must be the exact positive balance. This is to enforce that the caller is aware of the amount being cleared.* ```solidity function clear(Currency currency, uint256 amount) external onlyWhenUnlocked; ``` ### mint Called by the user to move value into ERC6909 balance *The id is converted to a uint160 to correspond to a currency address If the upper 12 bytes are not 0, they will be 0-ed out* ```solidity function mint(address to, uint256 id, uint256 amount) external onlyWhenUnlocked; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`to`|`address`|The address to mint the tokens to| |`id`|`uint256`|The currency address to mint to ERC6909s, as a uint256| |`amount`|`uint256`|The amount of currency to mint| ### burn Called by the user to move value from ERC6909 balance *The id is converted to a uint160 to correspond to a currency address If the upper 12 bytes are not 0, they will be 0-ed out* ```solidity function burn(address from, uint256 id, uint256 amount) external onlyWhenUnlocked; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`from`|`address`|The address to burn the tokens from| |`id`|`uint256`|The currency address to burn from ERC6909s, as a uint256| |`amount`|`uint256`|The amount of currency to burn| ### updateDynamicLPFee Updates the pools lp fees for the a pool that has enabled dynamic lp fees. *A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee* ```solidity function updateDynamicLPFee(PoolKey memory key, uint24 newDynamicLPFee) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The key of the pool to update dynamic LP fees for| |`newDynamicLPFee`|`uint24`|The new dynamic pool LP fee| ### _settle ```solidity function _settle(address recipient) internal returns (uint256 paid); ``` ### _accountDelta Adds a balance delta in a currency for a target address ```solidity function _accountDelta(Currency currency, int128 delta, address target) internal; ``` ### _accountPoolBalanceDelta Accounts the deltas of 2 currencies to a target address ```solidity function _accountPoolBalanceDelta(PoolKey memory key, BalanceDelta delta, address target) internal; ``` ### _getPool Implementation of the _getPool function defined in ProtocolFees ```solidity function _getPool(PoolId id) internal view override returns (Pool.State storage); ``` ### _isUnlocked Implementation of the _isUnlocked function defined in ProtocolFees ```solidity function _isUnlocked() internal view override returns (bool); ``` --- ## ProtocolFees [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/ProtocolFees.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IProtocolFees](contracts/v4/reference/core/interfaces/IProtocolFees.md), Owned Contract handling the setting and accrual of protocol fees ## State Variables ### protocolFeesAccrued Given a currency address, returns the protocol fees accrued in that currency ```solidity mapping(Currency currency => uint256 amount) public protocolFeesAccrued; ``` ### protocolFeeController Returns the current protocol fee controller address ```solidity address public protocolFeeController; ``` ## Functions ### constructor ```solidity constructor(address initialOwner) Owned(initialOwner); ``` ### setProtocolFeeController Sets the protocol fee controller ```solidity function setProtocolFeeController(address controller) external onlyOwner; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`controller`|`address`|The new protocol fee controller| ### setProtocolFee Sets the protocol fee for the given pool ```solidity function setProtocolFee(PoolKey memory key, uint24 newProtocolFee) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The key of the pool to set a protocol fee for| |`newProtocolFee`|`uint24`|The fee to set| ### collectProtocolFees Collects the protocol fees for a given recipient and currency, returning the amount collected *This will revert if the contract is unlocked* ```solidity function collectProtocolFees(address recipient, Currency currency, uint256 amount) external returns (uint256 amountCollected); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`recipient`|`address`|The address to receive the protocol fees| |`currency`|`Currency`|The currency to withdraw| |`amount`|`uint256`|The amount of currency to withdraw| **Returns** |Name|Type|Description| |----|----|-----------| |`amountCollected`|`uint256`|The amount of currency successfully withdrawn| ### _isUnlocked *abstract internal function to allow the ProtocolFees contract to access the lock* ```solidity function _isUnlocked() internal virtual returns (bool); ``` ### _getPool *abstract internal function to allow the ProtocolFees contract to access pool state* *this is overridden in PoolManager.sol to give access to the _pools mapping* ```solidity function _getPool(PoolId id) internal virtual returns (Pool.State storage); ``` ### _updateProtocolFees ```solidity function _updateProtocolFees(Currency currency, uint256 amount) internal; ``` --- ## IERC20Minimal [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/interfaces/external/IERC20Minimal.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Contains a subset of the full ERC20 interface that is used in Uniswap V3 ## Functions ### balanceOf Returns an account's balance in the token ```solidity function balanceOf(address account) external view returns (uint256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`account`|`address`|The account for which to look up the number of tokens it has, i.e. its balance| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|The number of tokens held by the account| ### transfer Transfers the amount of token from the `msg.sender` to the recipient ```solidity function transfer(address recipient, uint256 amount) external returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`recipient`|`address`|The account that will receive the amount transferred| |`amount`|`uint256`|The number of tokens to send from the sender to the recipient| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|Returns true for a successful transfer, false for an unsuccessful transfer| ### allowance Returns the current allowance given to a spender by an owner ```solidity function allowance(address owner, address spender) external view returns (uint256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|The account of the token owner| |`spender`|`address`|The account of the token spender| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|The current allowance granted by `owner` to `spender`| ### approve Sets the allowance of a spender from the `msg.sender` to the value `amount` ```solidity function approve(address spender, uint256 amount) external returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`spender`|`address`|The account which will be allowed to spend a given amount of the owners tokens| |`amount`|`uint256`|The amount of tokens allowed to be used by `spender`| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|Returns true for a successful approval, false for unsuccessful| ### transferFrom Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` ```solidity function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The account from which the transfer will be initiated| |`recipient`|`address`|The recipient of the transfer| |`amount`|`uint256`|The amount of the transfer| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|Returns true for a successful transfer, false for unsuccessful| ## Events ### Transfer Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. ```solidity event Transfer(address indexed from, address indexed to, uint256 value); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`from`|`address`|The account from which the tokens were sent, i.e. the balance decreased| |`to`|`address`|The account to which the tokens were sent, i.e. the balance increased| |`value`|`uint256`|The amount of tokens that were transferred| ### Approval Event emitted when the approval amount for the spender of a given owner's tokens changes. ```solidity event Approval(address indexed owner, address indexed spender, uint256 value); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|The account that approved spending of its tokens| |`spender`|`address`|The account for which the spending allowance was modified| |`value`|`uint256`|The new allowance from the owner to the spender| --- ## IERC6909Claims [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/interfaces/external/IERC6909Claims.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for claims over a contract balance, wrapped as a ERC6909 ## Functions ### balanceOf Owner balance of an id. ```solidity function balanceOf(address owner, uint256 id) external view returns (uint256 amount); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|The address of the owner.| |`id`|`uint256`|The id of the token.| **Returns** |Name|Type|Description| |----|----|-----------| |`amount`|`uint256`|The balance of the token.| ### allowance Spender allowance of an id. ```solidity function allowance(address owner, address spender, uint256 id) external view returns (uint256 amount); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|The address of the owner.| |`spender`|`address`|The address of the spender.| |`id`|`uint256`|The id of the token.| **Returns** |Name|Type|Description| |----|----|-----------| |`amount`|`uint256`|The allowance of the token.| ### isOperator Checks if a spender is approved by an owner as an operator ```solidity function isOperator(address owner, address spender) external view returns (bool approved); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|The address of the owner.| |`spender`|`address`|The address of the spender.| **Returns** |Name|Type|Description| |----|----|-----------| |`approved`|`bool`|The approval status.| ### transfer Transfers an amount of an id from the caller to a receiver. ```solidity function transfer(address receiver, uint256 id, uint256 amount) external returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`receiver`|`address`|The address of the receiver.| |`id`|`uint256`|The id of the token.| |`amount`|`uint256`|The amount of the token.| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|bool True, always, unless the function reverts| ### transferFrom Transfers an amount of an id from a sender to a receiver. ```solidity function transferFrom(address sender, address receiver, uint256 id, uint256 amount) external returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The address of the sender.| |`receiver`|`address`|The address of the receiver.| |`id`|`uint256`|The id of the token.| |`amount`|`uint256`|The amount of the token.| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|bool True, always, unless the function reverts| ### approve Approves an amount of an id to a spender. ```solidity function approve(address spender, uint256 id, uint256 amount) external returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`spender`|`address`|The address of the spender.| |`id`|`uint256`|The id of the token.| |`amount`|`uint256`|The amount of the token.| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|bool True, always| ### setOperator Sets or removes an operator for the caller. ```solidity function setOperator(address operator, bool approved) external returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`operator`|`address`|The address of the operator.| |`approved`|`bool`|The approval status.| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|bool True, always| ## Events ### OperatorSet ```solidity event OperatorSet(address indexed owner, address indexed operator, bool approved); ``` ### Approval ```solidity event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount); ``` ### Transfer ```solidity event Transfer(address caller, address indexed from, address indexed to, uint256 indexed id, uint256 amount); ``` --- ## IExtsload [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/interfaces/IExtsload.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for functions to access any storage slot in a contract ## Functions ### extsload Called by external contracts to access granular pool state ```solidity function extsload(bytes32 slot) external view returns (bytes32 value); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`slot`|`bytes32`|Key of slot to sload| **Returns** |Name|Type|Description| |----|----|-----------| |`value`|`bytes32`|The value of the slot as bytes32| ### extsload Called by external contracts to access granular pool state ```solidity function extsload(bytes32 startSlot, uint256 nSlots) external view returns (bytes32[] memory values); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`startSlot`|`bytes32`|Key of slot to start sloading from| |`nSlots`|`uint256`|Number of slots to load into return value| **Returns** |Name|Type|Description| |----|----|-----------| |`values`|`bytes32[]`|List of loaded values.| ### extsload Called by external contracts to access sparse pool state ```solidity function extsload(bytes32[] calldata slots) external view returns (bytes32[] memory values); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`slots`|`bytes32[]`|List of slots to SLOAD from.| **Returns** |Name|Type|Description| |----|----|-----------| |`values`|`bytes32[]`|List of loaded values.| --- ## IExttload [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/interfaces/IExttload.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for functions to access any transient storage slot in a contract ## Functions ### exttload Called by external contracts to access transient storage of the contract ```solidity function exttload(bytes32 slot) external view returns (bytes32 value); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`slot`|`bytes32`|Key of slot to tload| **Returns** |Name|Type|Description| |----|----|-----------| |`value`|`bytes32`|The value of the slot as bytes32| ### exttload Called by external contracts to access sparse transient pool state ```solidity function exttload(bytes32[] calldata slots) external view returns (bytes32[] memory values); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`slots`|`bytes32[]`|List of slots to tload| **Returns** |Name|Type|Description| |----|----|-----------| |`values`|`bytes32[]`|List of loaded values| --- ## IHooks [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/interfaces/IHooks.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) V4 decides whether to invoke specific hooks by inspecting the least significant bits of the address that the hooks contract is deployed to. For example, a hooks contract deployed to address: 0x0000000000000000000000000000000000002400 has the lowest bits '10 0100 0000 0000' which would cause the 'before initialize' and 'after add liquidity' hooks to be used. See the Hooks library for the full spec. *Should only be callable by the v4 PoolManager.* ## Functions ### beforeInitialize The hook called before the state of a pool is initialized ```solidity function beforeInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96) external returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the initialize call| |`key`|`PoolKey`|The key for the pool being initialized| |`sqrtPriceX96`|`uint160`|The sqrt(price) of the pool as a Q64.96| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### afterInitialize The hook called after the state of a pool is initialized ```solidity function afterInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96, int24 tick) external returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the initialize call| |`key`|`PoolKey`|The key for the pool being initialized| |`sqrtPriceX96`|`uint160`|The sqrt(price) of the pool as a Q64.96| |`tick`|`int24`|The current tick after the state of a pool is initialized| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### beforeAddLiquidity The hook called before liquidity is added ```solidity function beforeAddLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, bytes calldata hookData ) external returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the add liquidity call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for adding liquidity| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the liquidity provider to be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### afterAddLiquidity The hook called after liquidity is added ```solidity function afterAddLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, BalanceDelta delta, BalanceDelta feesAccrued, bytes calldata hookData ) external returns (bytes4, BalanceDelta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the add liquidity call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for adding liquidity| |`delta`|`BalanceDelta`|The caller's balance delta after adding liquidity; the sum of principal delta, fees accrued, and hook delta| |`feesAccrued`|`BalanceDelta`|The fees accrued since the last time fees were collected from this position| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the liquidity provider to be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| |``|`BalanceDelta`|BalanceDelta The hook's delta in token0 and token1. Positive: the hook is owed/took currency, negative: the hook owes/sent currency| ### beforeRemoveLiquidity The hook called before liquidity is removed ```solidity function beforeRemoveLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, bytes calldata hookData ) external returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the remove liquidity call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for removing liquidity| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the liquidity provider to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### afterRemoveLiquidity The hook called after liquidity is removed ```solidity function afterRemoveLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, BalanceDelta delta, BalanceDelta feesAccrued, bytes calldata hookData ) external returns (bytes4, BalanceDelta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the remove liquidity call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for removing liquidity| |`delta`|`BalanceDelta`|The caller's balance delta after removing liquidity; the sum of principal delta, fees accrued, and hook delta| |`feesAccrued`|`BalanceDelta`|The fees accrued since the last time fees were collected from this position| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the liquidity provider to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| |``|`BalanceDelta`|BalanceDelta The hook's delta in token0 and token1. Positive: the hook is owed/took currency, negative: the hook owes/sent currency| ### beforeSwap The hook called before a swap ```solidity function beforeSwap( address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata hookData ) external returns (bytes4, BeforeSwapDelta, uint24); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the swap call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.SwapParams`|The parameters for the swap| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the swapper to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| |``|`BeforeSwapDelta`|BeforeSwapDelta The hook's delta in specified and unspecified currencies. Positive: the hook is owed/took currency, negative: the hook owes/sent currency| |``|`uint24`|uint24 Optionally override the lp fee, only used if three conditions are met: 1. the Pool has a dynamic fee, 2. the value's 2nd highest bit is set (23rd bit, 0x400000), and 3. the value is less than or equal to the maximum fee (1 million)| ### afterSwap The hook called after a swap ```solidity function afterSwap( address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, BalanceDelta delta, bytes calldata hookData ) external returns (bytes4, int128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the swap call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.SwapParams`|The parameters for the swap| |`delta`|`BalanceDelta`|The amount owed to the caller (positive) or owed to the pool (negative)| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the swapper to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| |``|`int128`|int128 The hook's delta in unspecified currency. Positive: the hook is owed/took currency, negative: the hook owes/sent currency| ### beforeDonate The hook called before donate ```solidity function beforeDonate(address sender, PoolKey calldata key, uint256 amount0, uint256 amount1, bytes calldata hookData) external returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the donate call| |`key`|`PoolKey`|The key for the pool| |`amount0`|`uint256`|The amount of token0 being donated| |`amount1`|`uint256`|The amount of token1 being donated| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the donor to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### afterDonate The hook called after donate ```solidity function afterDonate(address sender, PoolKey calldata key, uint256 amount0, uint256 amount1, bytes calldata hookData) external returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the donate call| |`key`|`PoolKey`|The key for the pool| |`amount0`|`uint256`|The amount of token0 being donated| |`amount1`|`uint256`|The amount of token1 being donated| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the donor to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| --- ## IPoolManager(Interfaces) [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/interfaces/IPoolManager.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IProtocolFees](contracts/v4/reference/core/interfaces/IProtocolFees.md), [IERC6909Claims](contracts/v4/reference/core/interfaces/IERC6909Claims.md), [IExtsload](contracts/v4/reference/core/interfaces/IExtsload.md), [IExttload](contracts/v4/reference/core/interfaces/IExttload.md) Interface for the PoolManager ## Functions ### unlock All interactions on the contract that account deltas require unlocking. A caller that calls `unlock` must implement `IUnlockCallback(msg.sender).unlockCallback(data)`, where they interact with the remaining functions on this contract. *The only functions callable without an unlocking are `initialize` and `updateDynamicLPFee`* ```solidity function unlock(bytes calldata data) external returns (bytes memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`data`|`bytes`|Any data to pass to the callback, via `IUnlockCallback(msg.sender).unlockCallback(data)`| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes`|The data returned by the call to `IUnlockCallback(msg.sender).unlockCallback(data)`| ### initialize Initialize the state for a given pool ID *A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee* ```solidity function initialize(PoolKey memory key, uint160 sqrtPriceX96) external returns (int24 tick); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool key for the pool to initialize| |`sqrtPriceX96`|`uint160`|The initial square root price| **Returns** |Name|Type|Description| |----|----|-----------| |`tick`|`int24`|The initial tick of the pool| ### modifyLiquidity Modify the liquidity for the given pool *Poke by calling with a zero liquidityDelta* *Note that feesAccrued can be artificially inflated by a malicious actor and integrators should be careful using the value For pools with a single liquidity position, actors can donate to themselves to inflate feeGrowthGlobal (and consequently feesAccrued) atomically donating and collecting fees in the same unlockCallback may make the inflated value more extreme* ```solidity function modifyLiquidity(PoolKey memory key, ModifyLiquidityParams memory params, bytes calldata hookData) external returns (BalanceDelta callerDelta, BalanceDelta feesAccrued); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool to modify liquidity in| |`params`|`ModifyLiquidityParams`|The parameters for modifying the liquidity| |`hookData`|`bytes`|The data to pass through to the add/removeLiquidity hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`callerDelta`|`BalanceDelta`|The balance delta of the caller of modifyLiquidity. This is the total of both principal, fee deltas, and hook deltas if applicable| |`feesAccrued`|`BalanceDelta`|The balance delta of the fees generated in the liquidity range. Returned for informational purposes| ### swap Swap against the given pool *Swapping on low liquidity pools may cause unexpected swap amounts when liquidity available is less than amountSpecified. Additionally note that if interacting with hooks that have the BEFORE_SWAP_RETURNS_DELTA_FLAG or AFTER_SWAP_RETURNS_DELTA_FLAG the hook may alter the swap input/output. Integrators should perform checks on the returned swapDelta.* ```solidity function swap(PoolKey memory key, SwapParams memory params, bytes calldata hookData) external returns (BalanceDelta swapDelta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool to swap in| |`params`|`SwapParams`|The parameters for swapping| |`hookData`|`bytes`|The data to pass through to the swap hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`swapDelta`|`BalanceDelta`|The balance delta of the address swapping| ### donate Donate the given currency amounts to the in-range liquidity providers of a pool *Calls to donate can be frontrun adding just-in-time liquidity, with the aim of receiving a portion donated funds. Donors should keep this in mind when designing donation mechanisms.* *This function donates to in-range LPs at slot0.tick. In certain edge-cases of the swap algorithm, the `sqrtPrice` of a pool can be at the lower boundary of tick `n`, but the `slot0.tick` of the pool is already `n - 1`. In this case a call to `donate` would donate to tick `n - 1` (slot0.tick) not tick `n` (getTickAtSqrtPrice(slot0.sqrtPriceX96)). Read the comments in `Pool.swap()` for more information about this.* ```solidity function donate(PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData) external returns (BalanceDelta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The key of the pool to donate to| |`amount0`|`uint256`|The amount of currency0 to donate| |`amount1`|`uint256`|The amount of currency1 to donate| |`hookData`|`bytes`|The data to pass through to the donate hooks| **Returns** |Name|Type|Description| |----|----|-----------| |``|`BalanceDelta`|BalanceDelta The delta of the caller after the donate| ### sync Writes the current ERC20 balance of the specified currency to transient storage This is used to checkpoint balances for the manager and derive deltas for the caller. *This MUST be called before any ERC20 tokens are sent into the contract, but can be skipped for native tokens because the amount to settle is determined by the sent value. However, if an ERC20 token has been synced and not settled, and the caller instead wants to settle native funds, this function can be called with the native currency to then be able to settle the native currency* ```solidity function sync(Currency currency) external; ``` ### take Called by the user to net out some value owed to the user *Will revert if the requested amount is not available, consider using `mint` instead* *Can also be used as a mechanism for free flash loans* ```solidity function take(Currency currency, address to, uint256 amount) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`Currency`|The currency to withdraw from the pool manager| |`to`|`address`|The address to withdraw to| |`amount`|`uint256`|The amount of currency to withdraw| ### settle Called by the user to pay what is owed ```solidity function settle() external payable returns (uint256 paid); ``` **Returns** |Name|Type|Description| |----|----|-----------| |`paid`|`uint256`|The amount of currency settled| ### settleFor Called by the user to pay on behalf of another address ```solidity function settleFor(address recipient) external payable returns (uint256 paid); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`recipient`|`address`|The address to credit for the payment| **Returns** |Name|Type|Description| |----|----|-----------| |`paid`|`uint256`|The amount of currency settled| ### clear WARNING - Any currency that is cleared, will be non-retrievable, and locked in the contract permanently. A call to clear will zero out a positive balance WITHOUT a corresponding transfer. *This could be used to clear a balance that is considered dust. Additionally, the amount must be the exact positive balance. This is to enforce that the caller is aware of the amount being cleared.* ```solidity function clear(Currency currency, uint256 amount) external; ``` ### mint Called by the user to move value into ERC6909 balance *The id is converted to a uint160 to correspond to a currency address If the upper 12 bytes are not 0, they will be 0-ed out* ```solidity function mint(address to, uint256 id, uint256 amount) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`to`|`address`|The address to mint the tokens to| |`id`|`uint256`|The currency address to mint to ERC6909s, as a uint256| |`amount`|`uint256`|The amount of currency to mint| ### burn Called by the user to move value from ERC6909 balance *The id is converted to a uint160 to correspond to a currency address If the upper 12 bytes are not 0, they will be 0-ed out* ```solidity function burn(address from, uint256 id, uint256 amount) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`from`|`address`|The address to burn the tokens from| |`id`|`uint256`|The currency address to burn from ERC6909s, as a uint256| |`amount`|`uint256`|The amount of currency to burn| ### updateDynamicLPFee Updates the pools lp fees for the a pool that has enabled dynamic lp fees. *A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee* ```solidity function updateDynamicLPFee(PoolKey memory key, uint24 newDynamicLPFee) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The key of the pool to update dynamic LP fees for| |`newDynamicLPFee`|`uint24`|The new dynamic pool LP fee| ## Events ### Initialize Emitted when a new pool is initialized ```solidity event Initialize( PoolId indexed id, Currency indexed currency0, Currency indexed currency1, uint24 fee, int24 tickSpacing, IHooks hooks, uint160 sqrtPriceX96, int24 tick ); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`id`|`PoolId`|The abi encoded hash of the pool key struct for the new pool| |`currency0`|`Currency`|The first currency of the pool by address sort order| |`currency1`|`Currency`|The second currency of the pool by address sort order| |`fee`|`uint24`|The fee collected upon every swap in the pool, denominated in hundredths of a bip| |`tickSpacing`|`int24`|The minimum number of ticks between initialized ticks| |`hooks`|`IHooks`|The hooks contract address for the pool, or address(0) if none| |`sqrtPriceX96`|`uint160`|The price of the pool on initialization| |`tick`|`int24`|The initial tick of the pool corresponding to the initialized price| ### ModifyLiquidity Emitted when a liquidity position is modified ```solidity event ModifyLiquidity( PoolId indexed id, address indexed sender, int24 tickLower, int24 tickUpper, int256 liquidityDelta, bytes32 salt ); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`id`|`PoolId`|The abi encoded hash of the pool key struct for the pool that was modified| |`sender`|`address`|The address that modified the pool| |`tickLower`|`int24`|The lower tick of the position| |`tickUpper`|`int24`|The upper tick of the position| |`liquidityDelta`|`int256`|The amount of liquidity that was added or removed| |`salt`|`bytes32`|The extra data to make positions unique| ### Swap Emitted for swaps between currency0 and currency1 ```solidity event Swap( PoolId indexed id, address indexed sender, int128 amount0, int128 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick, uint24 fee ); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`id`|`PoolId`|The abi encoded hash of the pool key struct for the pool that was modified| |`sender`|`address`|The address that initiated the swap call, and that received the callback| |`amount0`|`int128`|The delta of the currency0 balance of the pool| |`amount1`|`int128`|The delta of the currency1 balance of the pool| |`sqrtPriceX96`|`uint160`|The sqrt(price) of the pool after the swap, as a Q64.96| |`liquidity`|`uint128`|The liquidity of the pool after the swap| |`tick`|`int24`|The log base 1.0001 of the price of the pool after the swap| |`fee`|`uint24`|The swap fee in hundredths of a bip| ### Donate Emitted for donations ```solidity event Donate(PoolId indexed id, address indexed sender, uint256 amount0, uint256 amount1); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`id`|`PoolId`|The abi encoded hash of the pool key struct for the pool that was donated to| |`sender`|`address`|The address that initiated the donate call| |`amount0`|`uint256`|The amount donated in currency0| |`amount1`|`uint256`|The amount donated in currency1| ## Errors ### CurrencyNotSettled Thrown when a currency is not netted out after the contract is unlocked ```solidity error CurrencyNotSettled(); ``` ### PoolNotInitialized Thrown when trying to interact with a non-initialized pool ```solidity error PoolNotInitialized(); ``` ### AlreadyUnlocked Thrown when unlock is called, but the contract is already unlocked ```solidity error AlreadyUnlocked(); ``` ### ManagerLocked Thrown when a function is called that requires the contract to be unlocked, but it is not ```solidity error ManagerLocked(); ``` ### TickSpacingTooLarge Pools are limited to type(int16).max tickSpacing in #initialize, to prevent overflow ```solidity error TickSpacingTooLarge(int24 tickSpacing); ``` ### TickSpacingTooSmall Pools must have a positive non-zero tickSpacing passed to #initialize ```solidity error TickSpacingTooSmall(int24 tickSpacing); ``` ### CurrenciesOutOfOrderOrEqual PoolKey must have currencies where address(currency0) < address(currency1) ```solidity error CurrenciesOutOfOrderOrEqual(address currency0, address currency1); ``` ### UnauthorizedDynamicLPFeeUpdate Thrown when a call to updateDynamicLPFee is made by an address that is not the hook, or on a pool that does not have a dynamic swap fee. ```solidity error UnauthorizedDynamicLPFeeUpdate(); ``` ### SwapAmountCannotBeZero Thrown when trying to swap amount of 0 ```solidity error SwapAmountCannotBeZero(); ``` ### NonzeroNativeValue Thrown when native currency is passed to a non native settlement ```solidity error NonzeroNativeValue(); ``` ### MustClearExactPositiveDelta Thrown when `clear` is called with an amount that is not exactly equal to the open currency delta. ```solidity error MustClearExactPositiveDelta(); ``` ## Structs ### ModifyLiquidityParams ```solidity struct ModifyLiquidityParams { int24 tickLower; int24 tickUpper; int256 liquidityDelta; bytes32 salt; } ``` ### SwapParams ```solidity struct SwapParams { bool zeroForOne; int256 amountSpecified; uint160 sqrtPriceLimitX96; } ``` --- ## IProtocolFees [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/interfaces/IProtocolFees.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for all protocol-fee related functions in the pool manager ## Functions ### protocolFeesAccrued Given a currency address, returns the protocol fees accrued in that currency ```solidity function protocolFeesAccrued(Currency currency) external view returns (uint256 amount); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`Currency`|The currency to check| **Returns** |Name|Type|Description| |----|----|-----------| |`amount`|`uint256`|The amount of protocol fees accrued in the currency| ### setProtocolFee Sets the protocol fee for the given pool ```solidity function setProtocolFee(PoolKey memory key, uint24 newProtocolFee) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The key of the pool to set a protocol fee for| |`newProtocolFee`|`uint24`|The fee to set| ### setProtocolFeeController Sets the protocol fee controller ```solidity function setProtocolFeeController(address controller) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`controller`|`address`|The new protocol fee controller| ### collectProtocolFees Collects the protocol fees for a given recipient and currency, returning the amount collected *This will revert if the contract is unlocked* ```solidity function collectProtocolFees(address recipient, Currency currency, uint256 amount) external returns (uint256 amountCollected); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`recipient`|`address`|The address to receive the protocol fees| |`currency`|`Currency`|The currency to withdraw| |`amount`|`uint256`|The amount of currency to withdraw| **Returns** |Name|Type|Description| |----|----|-----------| |`amountCollected`|`uint256`|The amount of currency successfully withdrawn| ### protocolFeeController Returns the current protocol fee controller address ```solidity function protocolFeeController() external view returns (address); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`address`|address The current protocol fee controller address| ## Events ### ProtocolFeeControllerUpdated Emitted when the protocol fee controller address is updated in setProtocolFeeController. ```solidity event ProtocolFeeControllerUpdated(address indexed protocolFeeController); ``` ### ProtocolFeeUpdated Emitted when the protocol fee is updated for a pool. ```solidity event ProtocolFeeUpdated(PoolId indexed id, uint24 protocolFee); ``` ## Errors ### ProtocolFeeTooLarge Thrown when protocol fee is set too high ```solidity error ProtocolFeeTooLarge(uint24 fee); ``` ### InvalidCaller Thrown when collectProtocolFees or setProtocolFee is not called by the controller. ```solidity error InvalidCaller(); ``` ### ProtocolFeeCurrencySynced Thrown when collectProtocolFees is attempted on a token that is synced. ```solidity error ProtocolFeeCurrencySynced(); ``` --- ## IUnlockCallback [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/interfaces/callback/IUnlockCallback.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the callback executed when an address unlocks the pool manager ## Functions ### unlockCallback Called by the pool manager on `msg.sender` when the manager is unlocked ```solidity function unlockCallback(bytes calldata data) external returns (bytes memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`data`|`bytes`|The data that was passed to the call to unlock| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes`|Any data that you want to be returned from the unlock call| --- ## BitMath [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/BitMath.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Author:** Solady (https://github.com/Vectorized/solady/blob/8200a70e8dc2a77ecb074fc2e99a2a0d36547522/src/utils/LibBit.sol) *This library provides functionality for computing bit properties of an unsigned integer* ## Functions ### mostSignificantBit Returns the index of the most significant bit of the number, where the least significant bit is at index 0 and the most significant bit is at index 255 ```solidity function mostSignificantBit(uint256 x) internal pure returns (uint8 r); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`uint256`|the value for which to compute the most significant bit, must be greater than 0| **Returns** |Name|Type|Description| |----|----|-----------| |`r`|`uint8`|the index of the most significant bit| ### leastSignificantBit Returns the index of the least significant bit of the number, where the least significant bit is at index 0 and the most significant bit is at index 255 ```solidity function leastSignificantBit(uint256 x) internal pure returns (uint8 r); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`uint256`|the value for which to compute the least significant bit, must be greater than 0| **Returns** |Name|Type|Description| |----|----|-----------| |`r`|`uint8`|the index of the least significant bit| --- ## CurrencyDelta [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/CurrencyDelta.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) *this library implements the equivalent of a mapping, as transient storage can only be accessed in assembly* ## Functions ### _computeSlot calculates which storage slot a delta should be stored in for a given account and currency ```solidity function _computeSlot(address target, Currency currency) internal pure returns (bytes32 hashSlot); ``` ### getDelta ```solidity function getDelta(Currency currency, address target) internal view returns (int256 delta); ``` ### applyDelta applies a new currency delta for a given account and currency ```solidity function applyDelta(Currency currency, address target, int128 delta) internal returns (int256 previous, int256 next); ``` **Returns** |Name|Type|Description| |----|----|-----------| |`previous`|`int256`|The prior value| |`next`|`int256`|The modified result| --- ## CurrencyReserves [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/CurrencyReserves.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## State Variables ### RESERVES_OF_SLOT bytes32(uint256(keccak256("ReservesOf")) - 1) ```solidity bytes32 constant RESERVES_OF_SLOT = 0x1e0745a7db1623981f0b2a5d4232364c00787266eb75ad546f190e6cebe9bd95; ``` ### CURRENCY_SLOT bytes32(uint256(keccak256("Currency")) - 1) ```solidity bytes32 constant CURRENCY_SLOT = 0x27e098c505d44ec3574004bca052aabf76bd35004c182099d8c575fb238593b9; ``` ## Functions ### getSyncedCurrency ```solidity function getSyncedCurrency() internal view returns (Currency currency); ``` ### resetCurrency ```solidity function resetCurrency() internal; ``` ### syncCurrencyAndReserves ```solidity function syncCurrencyAndReserves(Currency currency, uint256 value) internal; ``` ### getSyncedReserves ```solidity function getSyncedReserves() internal view returns (uint256 value); ``` --- ## CustomRevert [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/CustomRevert.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Contains functions for reverting with custom errors with different argument types efficiently *To use this library, declare `using CustomRevert for bytes4;` and replace `revert CustomError()` with `CustomError.selector.revertWith()`* *The functions may tamper with the free memory pointer but it is fine since the call context is exited immediately* ## Functions ### revertWith *Reverts with the selector of a custom error in the scratch space* ```solidity function revertWith(bytes4 selector) internal pure; ``` ### revertWith *Reverts with a custom error with an address argument in the scratch space* ```solidity function revertWith(bytes4 selector, address addr) internal pure; ``` ### revertWith *Reverts with a custom error with an int24 argument in the scratch space* ```solidity function revertWith(bytes4 selector, int24 value) internal pure; ``` ### revertWith *Reverts with a custom error with a uint160 argument in the scratch space* ```solidity function revertWith(bytes4 selector, uint160 value) internal pure; ``` ### revertWith *Reverts with a custom error with two int24 arguments* ```solidity function revertWith(bytes4 selector, int24 value1, int24 value2) internal pure; ``` ### revertWith *Reverts with a custom error with two uint160 arguments* ```solidity function revertWith(bytes4 selector, uint160 value1, uint160 value2) internal pure; ``` ### revertWith *Reverts with a custom error with two address arguments* ```solidity function revertWith(bytes4 selector, address value1, address value2) internal pure; ``` ### bubbleUpAndRevertWith bubble up the revert message returned by a call and revert with a wrapped ERC-7751 error *this method can be vulnerable to revert data bombs* ```solidity function bubbleUpAndRevertWith(address revertingContract, bytes4 revertingFunctionSelector, bytes4 additionalContext) internal pure; ``` ## Errors ### WrappedError *ERC-7751 error for wrapping bubbled up reverts* ```solidity error WrappedError(address target, bytes4 selector, bytes reason, bytes details); ``` --- ## FixedPoint128 [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/FixedPoint128.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) ## State Variables ### Q128 ```solidity uint256 internal constant Q128 = 0x100000000000000000000000000000000; ``` --- ## FixedPoint96 [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/FixedPoint96.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) *Used in SqrtPriceMath.sol* ## State Variables ### RESOLUTION ```solidity uint8 internal constant RESOLUTION = 96; ``` ### Q96 ```solidity uint256 internal constant Q96 = 0x1000000000000000000000000; ``` --- ## FullMath [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/FullMath.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision *Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits* ## Functions ### mulDiv Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 *Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv* ```solidity function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`a`|`uint256`|The multiplicand| |`b`|`uint256`|The multiplier| |`denominator`|`uint256`|The divisor| **Returns** |Name|Type|Description| |----|----|-----------| |`result`|`uint256`|The 256-bit result| ### mulDivRoundingUp Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 ```solidity function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`a`|`uint256`|The multiplicand| |`b`|`uint256`|The multiplier| |`denominator`|`uint256`|The divisor| **Returns** |Name|Type|Description| |----|----|-----------| |`result`|`uint256`|The 256-bit result| --- ## Hooks(Libraries) [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/Hooks.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) V4 decides whether to invoke specific hooks by inspecting the least significant bits of the address that the hooks contract is deployed to. For example, a hooks contract deployed to address: 0x0000000000000000000000000000000000002400 has the lowest bits '10 0100 0000 0000' which would cause the 'before initialize' and 'after add liquidity' hooks to be used. ## State Variables ### ALL_HOOK_MASK ```solidity uint160 internal constant ALL_HOOK_MASK = uint160((1 << 14) - 1); ``` ### BEFORE_INITIALIZE_FLAG ```solidity uint160 internal constant BEFORE_INITIALIZE_FLAG = 1 << 13; ``` ### AFTER_INITIALIZE_FLAG ```solidity uint160 internal constant AFTER_INITIALIZE_FLAG = 1 << 12; ``` ### BEFORE_ADD_LIQUIDITY_FLAG ```solidity uint160 internal constant BEFORE_ADD_LIQUIDITY_FLAG = 1 << 11; ``` ### AFTER_ADD_LIQUIDITY_FLAG ```solidity uint160 internal constant AFTER_ADD_LIQUIDITY_FLAG = 1 << 10; ``` ### BEFORE_REMOVE_LIQUIDITY_FLAG ```solidity uint160 internal constant BEFORE_REMOVE_LIQUIDITY_FLAG = 1 << 9; ``` ### AFTER_REMOVE_LIQUIDITY_FLAG ```solidity uint160 internal constant AFTER_REMOVE_LIQUIDITY_FLAG = 1 << 8; ``` ### BEFORE_SWAP_FLAG ```solidity uint160 internal constant BEFORE_SWAP_FLAG = 1 << 7; ``` ### AFTER_SWAP_FLAG ```solidity uint160 internal constant AFTER_SWAP_FLAG = 1 << 6; ``` ### BEFORE_DONATE_FLAG ```solidity uint160 internal constant BEFORE_DONATE_FLAG = 1 << 5; ``` ### AFTER_DONATE_FLAG ```solidity uint160 internal constant AFTER_DONATE_FLAG = 1 << 4; ``` ### BEFORE_SWAP_RETURNS_DELTA_FLAG ```solidity uint160 internal constant BEFORE_SWAP_RETURNS_DELTA_FLAG = 1 << 3; ``` ### AFTER_SWAP_RETURNS_DELTA_FLAG ```solidity uint160 internal constant AFTER_SWAP_RETURNS_DELTA_FLAG = 1 << 2; ``` ### AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG ```solidity uint160 internal constant AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG = 1 << 1; ``` ### AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG ```solidity uint160 internal constant AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG = 1 << 0; ``` ## Functions ### validateHookPermissions Utility function intended to be used in hook constructors to ensure the deployed hooks address causes the intended hooks to be called *permissions param is memory as the function will be called from constructors* ```solidity function validateHookPermissions(IHooks self, Permissions memory permissions) internal pure; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`IHooks`|| |`permissions`|`Permissions`|The hooks that are intended to be called| ### isValidHookAddress Ensures that the hook address includes at least one hook flag or dynamic fees, or is the 0 address ```solidity function isValidHookAddress(IHooks self, uint24 fee) internal pure returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`IHooks`|The hook to verify| |`fee`|`uint24`|The fee of the pool the hook is used with| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|bool True if the hook address is valid| ### callHook performs a hook call using the given calldata on the given hook that doesn't return a delta ```solidity function callHook(IHooks self, bytes memory data) internal returns (bytes memory result); ``` **Returns** |Name|Type|Description| |----|----|-----------| |`result`|`bytes`|The complete data returned by the hook| ### callHookWithReturnDelta performs a hook call using the given calldata on the given hook ```solidity function callHookWithReturnDelta(IHooks self, bytes memory data, bool parseReturn) internal returns (int256); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`int256`|int256 The delta returned by the hook| ### noSelfCall modifier to prevent calling a hook if they initiated the action ```solidity modifier noSelfCall(IHooks self); ``` ### beforeInitialize calls beforeInitialize hook if permissioned and validates return value ```solidity function beforeInitialize(IHooks self, PoolKey memory key, uint160 sqrtPriceX96) internal noSelfCall(self); ``` ### afterInitialize calls afterInitialize hook if permissioned and validates return value ```solidity function afterInitialize(IHooks self, PoolKey memory key, uint160 sqrtPriceX96, int24 tick) internal noSelfCall(self); ``` ### beforeModifyLiquidity calls beforeModifyLiquidity hook if permissioned and validates return value ```solidity function beforeModifyLiquidity( IHooks self, PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, bytes calldata hookData ) internal noSelfCall(self); ``` ### afterModifyLiquidity calls afterModifyLiquidity hook if permissioned and validates return value ```solidity function afterModifyLiquidity( IHooks self, PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, BalanceDelta delta, BalanceDelta feesAccrued, bytes calldata hookData ) internal returns (BalanceDelta callerDelta, BalanceDelta hookDelta); ``` ### beforeSwap calls beforeSwap hook if permissioned and validates return value ```solidity function beforeSwap(IHooks self, PoolKey memory key, IPoolManager.SwapParams memory params, bytes calldata hookData) internal returns (int256 amountToSwap, BeforeSwapDelta hookReturn, uint24 lpFeeOverride); ``` ### afterSwap calls afterSwap hook if permissioned and validates return value ```solidity function afterSwap( IHooks self, PoolKey memory key, IPoolManager.SwapParams memory params, BalanceDelta swapDelta, bytes calldata hookData, BeforeSwapDelta beforeSwapHookReturn ) internal returns (BalanceDelta, BalanceDelta); ``` ### beforeDonate calls beforeDonate hook if permissioned and validates return value ```solidity function beforeDonate(IHooks self, PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData) internal noSelfCall(self); ``` ### afterDonate calls afterDonate hook if permissioned and validates return value ```solidity function afterDonate(IHooks self, PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData) internal noSelfCall(self); ``` ### hasPermission ```solidity function hasPermission(IHooks self, uint160 flag) internal pure returns (bool); ``` ## Errors ### HookAddressNotValid Thrown if the address will not lead to the specified hook calls being called ```solidity error HookAddressNotValid(address hooks); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`hooks`|`address`|The address of the hooks contract| ### InvalidHookResponse Hook did not return its selector ```solidity error InvalidHookResponse(); ``` ### HookCallFailed Additional context for ERC-7751 wrapped error when a hook call fails ```solidity error HookCallFailed(); ``` ### HookDeltaExceedsSwapAmount The hook's delta changed the swap from exactIn to exactOut or vice versa ```solidity error HookDeltaExceedsSwapAmount(); ``` ## Structs ### Permissions ```solidity struct Permissions { bool beforeInitialize; bool afterInitialize; bool beforeAddLiquidity; bool afterAddLiquidity; bool beforeRemoveLiquidity; bool afterRemoveLiquidity; bool beforeSwap; bool afterSwap; bool beforeDonate; bool afterDonate; bool beforeSwapReturnDelta; bool afterSwapReturnDelta; bool afterAddLiquidityReturnDelta; bool afterRemoveLiquidityReturnDelta; } ``` --- ## LPFeeLibrary [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/LPFeeLibrary.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Library of helper functions for a pools LP fee ## State Variables ### DYNAMIC_FEE_FLAG An lp fee of exactly 0b1000000... signals a dynamic fee pool. This isn't a valid static fee as it is > MAX_LP_FEE ```solidity uint24 public constant DYNAMIC_FEE_FLAG = 0x800000; ``` ### OVERRIDE_FEE_FLAG the second bit of the fee returned by beforeSwap is used to signal if the stored LP fee should be overridden in this swap ```solidity uint24 public constant OVERRIDE_FEE_FLAG = 0x400000; ``` ### REMOVE_OVERRIDE_MASK mask to remove the override fee flag from a fee returned by the beforeSwaphook ```solidity uint24 public constant REMOVE_OVERRIDE_MASK = 0xBFFFFF; ``` ### MAX_LP_FEE the lp fee is represented in hundredths of a bip, so the max is 100% ```solidity uint24 public constant MAX_LP_FEE = 1000000; ``` ## Functions ### isDynamicFee returns true if a pool's LP fee signals that the pool has a dynamic fee ```solidity function isDynamicFee(uint24 self) internal pure returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`uint24`|The fee to check| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|bool True of the fee is dynamic| ### isValid returns true if an LP fee is valid, aka not above the maximum permitted fee ```solidity function isValid(uint24 self) internal pure returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`uint24`|The fee to check| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|bool True of the fee is valid| ### validate validates whether an LP fee is larger than the maximum, and reverts if invalid ```solidity function validate(uint24 self) internal pure; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`uint24`|The fee to validate| ### getInitialLPFee gets and validates the initial LP fee for a pool. Dynamic fee pools have an initial fee of 0. *if a dynamic fee pool wants a non-0 initial fee, it should call `updateDynamicLPFee` in the afterInitialize hook* ```solidity function getInitialLPFee(uint24 self) internal pure returns (uint24); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`uint24`|The fee to get the initial LP from| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint24`|initialFee 0 if the fee is dynamic, otherwise the fee (if valid)| ### isOverride returns true if the fee has the override flag set (2nd highest bit of the uint24) ```solidity function isOverride(uint24 self) internal pure returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`uint24`|The fee to check| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|bool True of the fee has the override flag set| ### removeOverrideFlag returns a fee with the override flag removed ```solidity function removeOverrideFlag(uint24 self) internal pure returns (uint24); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`uint24`|The fee to remove the override flag from| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint24`|fee The fee without the override flag set| ### removeOverrideFlagAndValidate Removes the override flag and validates the fee (reverts if the fee is too large) ```solidity function removeOverrideFlagAndValidate(uint24 self) internal pure returns (uint24 fee); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`uint24`|The fee to remove the override flag from, and then validate| **Returns** |Name|Type|Description| |----|----|-----------| |`fee`|`uint24`|The fee without the override flag set (if valid)| ## Errors ### LPFeeTooLarge Thrown when the static or dynamic fee on a pool exceeds 100%. ```solidity error LPFeeTooLarge(uint24 fee); ``` --- ## LiquidityMath [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/LiquidityMath.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## Functions ### addDelta Add a signed liquidity delta to liquidity and revert if it overflows or underflows ```solidity function addDelta(uint128 x, int128 y) internal pure returns (uint128 z); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`uint128`|The liquidity before change| |`y`|`int128`|The delta by which liquidity should be changed| **Returns** |Name|Type|Description| |----|----|-----------| |`z`|`uint128`|The liquidity delta| --- ## Lock [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/Lock.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) This is a temporary library that allows us to use transient storage (tstore/tload) TODO: This library can be deleted when we have the transient keyword support in solidity. ## State Variables ### IS_UNLOCKED_SLOT ```solidity bytes32 internal constant IS_UNLOCKED_SLOT = 0xc090fc4683624cfc3884e9d8de5eca132f2d0ec062aff75d43c0465d5ceeab23; ``` ## Functions ### unlock ```solidity function unlock() internal; ``` ### lock ```solidity function lock() internal; ``` ### isUnlocked ```solidity function isUnlocked() internal view returns (bool unlocked); ``` --- ## NonzeroDeltaCount [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/NonzeroDeltaCount.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) This is a temporary library that allows us to use transient storage (tstore/tload) for the nonzero delta count. TODO: This library can be deleted when we have the transient keyword support in solidity. ## State Variables ### NONZERO_DELTA_COUNT_SLOT ```solidity bytes32 internal constant NONZERO_DELTA_COUNT_SLOT = 0x7d4b3164c6e45b97e7d87b7125a44c5828d005af88f9d751cfd78729c5d99a0b; ``` ## Functions ### read ```solidity function read() internal view returns (uint256 count); ``` ### increment ```solidity function increment() internal; ``` ### decrement Potential to underflow. Ensure checks are performed by integrating contracts to ensure this does not happen. Current usage ensures this will not happen because we call decrement with known boundaries (only up to the number of times we call increment). ```solidity function decrement() internal; ``` --- ## ParseBytes [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/ParseBytes.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Parses bytes returned from hooks and the byte selector used to check return selectors from hooks. *parseSelector also is used to parse the expected selector For parsing hook returns, note that all hooks return either bytes4 or (bytes4, 32-byte-delta) or (bytes4, 32-byte-delta, uint24).* ## Functions ### parseSelector ```solidity function parseSelector(bytes memory result) internal pure returns (bytes4 selector); ``` ### parseFee ```solidity function parseFee(bytes memory result) internal pure returns (uint24 lpFee); ``` ### parseReturnDelta ```solidity function parseReturnDelta(bytes memory result) internal pure returns (int256 hookReturn); ``` --- ## Pool(Libraries) [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/Pool.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) a library with all actions that can be performed on a pool ## Functions ### checkTicks *Common checks for valid tick inputs.* ```solidity function checkTicks(int24 tickLower, int24 tickUpper) private pure; ``` ### initialize ```solidity function initialize(State storage self, uint160 sqrtPriceX96, uint24 lpFee) internal returns (int24 tick); ``` ### setProtocolFee ```solidity function setProtocolFee(State storage self, uint24 protocolFee) internal; ``` ### setLPFee Only dynamic fee pools may update the lp fee. ```solidity function setLPFee(State storage self, uint24 lpFee) internal; ``` ### modifyLiquidity Effect changes to a position in a pool *PoolManager checks that the pool is initialized before calling* ```solidity function modifyLiquidity(State storage self, ModifyLiquidityParams memory params) internal returns (BalanceDelta delta, BalanceDelta feeDelta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`State`|| |`params`|`ModifyLiquidityParams`|the position details and the change to the position's liquidity to effect| **Returns** |Name|Type|Description| |----|----|-----------| |`delta`|`BalanceDelta`|the deltas of the token balances of the pool, from the liquidity change| |`feeDelta`|`BalanceDelta`|the fees generated by the liquidity range| ### swap Executes a swap against the state, and returns the amount deltas of the pool *PoolManager checks that the pool is initialized before calling* ```solidity function swap(State storage self, SwapParams memory params) internal returns (BalanceDelta swapDelta, uint256 amountToProtocol, uint24 swapFee, SwapResult memory result); ``` ### donate Donates the given amount of currency0 and currency1 to the pool ```solidity function donate(State storage state, uint256 amount0, uint256 amount1) internal returns (BalanceDelta delta); ``` ### getFeeGrowthInside Retrieves fee growth data ```solidity function getFeeGrowthInside(State storage self, int24 tickLower, int24 tickUpper) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`State`|The Pool state struct| |`tickLower`|`int24`|The lower tick boundary of the position| |`tickUpper`|`int24`|The upper tick boundary of the position| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthInside0X128`|`uint256`|The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries| |`feeGrowthInside1X128`|`uint256`|The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries| ### updateTick Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa ```solidity function updateTick(State storage self, int24 tick, int128 liquidityDelta, bool upper) internal returns (bool flipped, uint128 liquidityGrossAfter); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`State`|The mapping containing all tick information for initialized ticks| |`tick`|`int24`|The tick that will be updated| |`liquidityDelta`|`int128`|A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left)| |`upper`|`bool`|true for updating a position's upper tick, or false for updating a position's lower tick| **Returns** |Name|Type|Description| |----|----|-----------| |`flipped`|`bool`|Whether the tick was flipped from initialized to uninitialized, or vice versa| |`liquidityGrossAfter`|`uint128`|The total amount of liquidity for all positions that references the tick after the update| ### tickSpacingToMaxLiquidityPerTick Derives max liquidity per tick from given tick spacing *Executed when adding liquidity* ```solidity function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128 result); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tickSpacing`|`int24`|The amount of required tick separation, realized in multiples of `tickSpacing` e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ...| **Returns** |Name|Type|Description| |----|----|-----------| |`result`|`uint128`|The max liquidity per tick| ### checkPoolInitialized Reverts if the given pool has not been initialized ```solidity function checkPoolInitialized(State storage self) internal view; ``` ### clearTick Clears tick data ```solidity function clearTick(State storage self, int24 tick) internal; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`State`|The mapping containing all initialized tick information for initialized ticks| |`tick`|`int24`|The tick that will be cleared| ### crossTick Transitions to next tick as needed by price movement ```solidity function crossTick(State storage self, int24 tick, uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128) internal returns (int128 liquidityNet); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`State`|The Pool state struct| |`tick`|`int24`|The destination tick of the transition| |`feeGrowthGlobal0X128`|`uint256`|The all-time global fee growth, per unit of liquidity, in token0| |`feeGrowthGlobal1X128`|`uint256`|The all-time global fee growth, per unit of liquidity, in token1| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidityNet`|`int128`|The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left)| ## Errors ### TicksMisordered Thrown when tickLower is not below tickUpper ```solidity error TicksMisordered(int24 tickLower, int24 tickUpper); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tickLower`|`int24`|The invalid tickLower| |`tickUpper`|`int24`|The invalid tickUpper| ### TickLowerOutOfBounds Thrown when tickLower is less than min tick ```solidity error TickLowerOutOfBounds(int24 tickLower); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tickLower`|`int24`|The invalid tickLower| ### TickUpperOutOfBounds Thrown when tickUpper exceeds max tick ```solidity error TickUpperOutOfBounds(int24 tickUpper); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tickUpper`|`int24`|The invalid tickUpper| ### TickLiquidityOverflow For the tick spacing, the tick has too much liquidity ```solidity error TickLiquidityOverflow(int24 tick); ``` ### PoolAlreadyInitialized Thrown when trying to initialize an already initialized pool ```solidity error PoolAlreadyInitialized(); ``` ### PoolNotInitialized Thrown when trying to interact with a non-initialized pool ```solidity error PoolNotInitialized(); ``` ### PriceLimitAlreadyExceeded Thrown when sqrtPriceLimitX96 on a swap has already exceeded its limit ```solidity error PriceLimitAlreadyExceeded(uint160 sqrtPriceCurrentX96, uint160 sqrtPriceLimitX96); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceCurrentX96`|`uint160`|The invalid, already surpassed sqrtPriceLimitX96| |`sqrtPriceLimitX96`|`uint160`|The surpassed price limit| ### PriceLimitOutOfBounds Thrown when sqrtPriceLimitX96 lies outside of valid tick/price range ```solidity error PriceLimitOutOfBounds(uint160 sqrtPriceLimitX96); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceLimitX96`|`uint160`|The invalid, out-of-bounds sqrtPriceLimitX96| ### NoLiquidityToReceiveFees Thrown by donate if there is currently 0 liquidity, since the fees will not go to any liquidity providers ```solidity error NoLiquidityToReceiveFees(); ``` ### InvalidFeeForExactOut Thrown when trying to swap with max lp fee and specifying an output amount ```solidity error InvalidFeeForExactOut(); ``` ## Structs ### TickInfo ```solidity struct TickInfo { uint128 liquidityGross; int128 liquidityNet; uint256 feeGrowthOutside0X128; uint256 feeGrowthOutside1X128; } ``` ### State The state of a pool *Note that feeGrowthGlobal can be artificially inflated For pools with a single liquidity position, actors can donate to themselves to freely inflate feeGrowthGlobal atomically donating and collecting fees in the same unlockCallback may make the inflated value more extreme* ```solidity struct State { Slot0 slot0; uint256 feeGrowthGlobal0X128; uint256 feeGrowthGlobal1X128; uint128 liquidity; mapping(int24 tick => TickInfo) ticks; mapping(int16 wordPos => uint256) tickBitmap; mapping(bytes32 positionKey => Position.State) positions; } ``` ### ModifyLiquidityParams ```solidity struct ModifyLiquidityParams { address owner; int24 tickLower; int24 tickUpper; int128 liquidityDelta; int24 tickSpacing; bytes32 salt; } ``` ### ModifyLiquidityState ```solidity struct ModifyLiquidityState { bool flippedLower; uint128 liquidityGrossAfterLower; bool flippedUpper; uint128 liquidityGrossAfterUpper; } ``` ### SwapResult ```solidity struct SwapResult { uint160 sqrtPriceX96; int24 tick; uint128 liquidity; } ``` ### StepComputations ```solidity struct StepComputations { uint160 sqrtPriceStartX96; int24 tickNext; bool initialized; uint160 sqrtPriceNextX96; uint256 amountIn; uint256 amountOut; uint256 feeAmount; uint256 feeGrowthGlobalX128; } ``` ### SwapParams ```solidity struct SwapParams { int256 amountSpecified; int24 tickSpacing; bool zeroForOne; uint160 sqrtPriceLimitX96; uint24 lpFeeOverride; } ``` --- ## Position(Libraries) [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/Position.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Positions represent an owner address' liquidity between a lower and upper tick boundary *Positions store additional state for tracking fees owed to the position* ## Functions ### get Returns the State struct of a position, given an owner and position boundaries ```solidity function get(mapping(bytes32 => State) storage self, address owner, int24 tickLower, int24 tickUpper, bytes32 salt) internal view returns (State storage position); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`mapping(bytes32 => State)`|The mapping containing all user positions| |`owner`|`address`|The address of the position owner| |`tickLower`|`int24`|The lower tick boundary of the position| |`tickUpper`|`int24`|The upper tick boundary of the position| |`salt`|`bytes32`|A unique value to differentiate between multiple positions in the same range| **Returns** |Name|Type|Description| |----|----|-----------| |`position`|`State`|The position info struct of the given owners' position| ### calculatePositionKey A helper function to calculate the position key ```solidity function calculatePositionKey(address owner, int24 tickLower, int24 tickUpper, bytes32 salt) internal pure returns (bytes32 positionKey); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|The address of the position owner| |`tickLower`|`int24`|the lower tick boundary of the position| |`tickUpper`|`int24`|the upper tick boundary of the position| |`salt`|`bytes32`|A unique value to differentiate between multiple positions in the same range, by the same owner. Passed in by the caller.| ### update Credits accumulated fees to a user's position ```solidity function update(State storage self, int128 liquidityDelta, uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) internal returns (uint256 feesOwed0, uint256 feesOwed1); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`State`|The individual position to update| |`liquidityDelta`|`int128`|The change in pool liquidity as a result of the position update| |`feeGrowthInside0X128`|`uint256`|The all-time fee growth in currency0, per unit of liquidity, inside the position's tick boundaries| |`feeGrowthInside1X128`|`uint256`|The all-time fee growth in currency1, per unit of liquidity, inside the position's tick boundaries| **Returns** |Name|Type|Description| |----|----|-----------| |`feesOwed0`|`uint256`|The amount of currency0 owed to the position owner| |`feesOwed1`|`uint256`|The amount of currency1 owed to the position owner| ## Errors ### CannotUpdateEmptyPosition Cannot update a position with no liquidity ```solidity error CannotUpdateEmptyPosition(); ``` ## Structs ### State ```solidity struct State { uint128 liquidity; uint256 feeGrowthInside0LastX128; uint256 feeGrowthInside1LastX128; } ``` --- ## ProtocolFeeLibrary [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/ProtocolFeeLibrary.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) library of functions related to protocol fees ## State Variables ### MAX_PROTOCOL_FEE Max protocol fee is 0.1% (1000 pips) *Increasing these values could lead to overflow in Pool.swap* ```solidity uint16 public constant MAX_PROTOCOL_FEE = 1000; ``` ### FEE_0_THRESHOLD Thresholds used for optimized bounds checks on protocol fees ```solidity uint24 internal constant FEE_0_THRESHOLD = 1001; ``` ### FEE_1_THRESHOLD ```solidity uint24 internal constant FEE_1_THRESHOLD = 1001 << 12; ``` ### PIPS_DENOMINATOR the protocol fee is represented in hundredths of a bip ```solidity uint256 internal constant PIPS_DENOMINATOR = 1_000_000; ``` ## Functions ### getZeroForOneFee ```solidity function getZeroForOneFee(uint24 self) internal pure returns (uint16); ``` ### getOneForZeroFee ```solidity function getOneForZeroFee(uint24 self) internal pure returns (uint16); ``` ### isValidProtocolFee ```solidity function isValidProtocolFee(uint24 self) internal pure returns (bool valid); ``` ### calculateSwapFee *here `self` is just a single direction's protocol fee, not a packed type of 2 protocol fees* ```solidity function calculateSwapFee(uint16 self, uint24 lpFee) internal pure returns (uint24 swapFee); ``` --- ## SafeCast [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/SafeCast.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Contains methods for safely casting between types ## Functions ### toUint160 Cast a uint256 to a uint160, revert on overflow ```solidity function toUint160(uint256 x) internal pure returns (uint160 y); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`uint256`|The uint256 to be downcasted| **Returns** |Name|Type|Description| |----|----|-----------| |`y`|`uint160`|The downcasted integer, now type uint160| ### toUint128 Cast a uint256 to a uint128, revert on overflow ```solidity function toUint128(uint256 x) internal pure returns (uint128 y); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`uint256`|The uint256 to be downcasted| **Returns** |Name|Type|Description| |----|----|-----------| |`y`|`uint128`|The downcasted integer, now type uint128| ### toUint128 Cast a int128 to a uint128, revert on overflow or underflow ```solidity function toUint128(int128 x) internal pure returns (uint128 y); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`int128`|The int128 to be casted| **Returns** |Name|Type|Description| |----|----|-----------| |`y`|`uint128`|The casted integer, now type uint128| ### toInt128 Cast a int256 to a int128, revert on overflow or underflow ```solidity function toInt128(int256 x) internal pure returns (int128 y); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`int256`|The int256 to be downcasted| **Returns** |Name|Type|Description| |----|----|-----------| |`y`|`int128`|The downcasted integer, now type int128| ### toInt256 Cast a uint256 to a int256, revert on overflow ```solidity function toInt256(uint256 x) internal pure returns (int256 y); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`uint256`|The uint256 to be casted| **Returns** |Name|Type|Description| |----|----|-----------| |`y`|`int256`|The casted integer, now type int256| ### toInt128 Cast a uint256 to a int128, revert on overflow ```solidity function toInt128(uint256 x) internal pure returns (int128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`uint256`|The uint256 to be downcasted| **Returns** |Name|Type|Description| |----|----|-----------| |``|`int128`|The downcasted integer, now type int128| ## Errors ### SafeCastOverflow ```solidity error SafeCastOverflow(); ``` --- ## SqrtPriceMath [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/SqrtPriceMath.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas ## Functions ### getNextSqrtPriceFromAmount0RoundingUp Gets the next sqrt price given a delta of currency0 *Always rounds up, because in the exact output case (increasing price) we need to move the price at least far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the price less in order to not send too much output. The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96), if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).* ```solidity function getNextSqrtPriceFromAmount0RoundingUp(uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add) internal pure returns (uint160); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPX96`|`uint160`|The starting price, i.e. before accounting for the currency0 delta| |`liquidity`|`uint128`|The amount of usable liquidity| |`amount`|`uint256`|How much of currency0 to add or remove from virtual reserves| |`add`|`bool`|Whether to add or remove the amount of currency0| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint160`|The price after adding or removing amount, depending on add| ### getNextSqrtPriceFromAmount1RoundingDown Gets the next sqrt price given a delta of currency1 *Always rounds down, because in the exact output case (decreasing price) we need to move the price at least far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the price less in order to not send too much output. The formula we compute is within \<1 wei of the lossless version: sqrtPX96 +- amount / liquidity* ```solidity function getNextSqrtPriceFromAmount1RoundingDown(uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add) internal pure returns (uint160); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPX96`|`uint160`|The starting price, i.e., before accounting for the currency1 delta| |`liquidity`|`uint128`|The amount of usable liquidity| |`amount`|`uint256`|How much of currency1 to add, or remove, from virtual reserves| |`add`|`bool`|Whether to add, or remove, the amount of currency1| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint160`|The price after adding or removing `amount`| ### getNextSqrtPriceFromInput Gets the next sqrt price given an input amount of currency0 or currency1 *Throws if price or liquidity are 0, or if the next price is out of bounds* ```solidity function getNextSqrtPriceFromInput(uint160 sqrtPX96, uint128 liquidity, uint256 amountIn, bool zeroForOne) internal pure returns (uint160); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPX96`|`uint160`|The starting price, i.e., before accounting for the input amount| |`liquidity`|`uint128`|The amount of usable liquidity| |`amountIn`|`uint256`|How much of currency0, or currency1, is being swapped in| |`zeroForOne`|`bool`|Whether the amount in is currency0 or currency1| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint160`|uint160 The price after adding the input amount to currency0 or currency1| ### getNextSqrtPriceFromOutput Gets the next sqrt price given an output amount of currency0 or currency1 *Throws if price or liquidity are 0 or the next price is out of bounds* ```solidity function getNextSqrtPriceFromOutput(uint160 sqrtPX96, uint128 liquidity, uint256 amountOut, bool zeroForOne) internal pure returns (uint160); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPX96`|`uint160`|The starting price before accounting for the output amount| |`liquidity`|`uint128`|The amount of usable liquidity| |`amountOut`|`uint256`|How much of currency0, or currency1, is being swapped out| |`zeroForOne`|`bool`|Whether the amount out is currency1 or currency0| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint160`|uint160 The price after removing the output amount of currency0 or currency1| ### getAmount0Delta Gets the amount0 delta between two prices *Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))* ```solidity function getAmount0Delta(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint128 liquidity, bool roundUp) internal pure returns (uint256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceAX96`|`uint160`|A sqrt price| |`sqrtPriceBX96`|`uint160`|Another sqrt price| |`liquidity`|`uint128`|The amount of usable liquidity| |`roundUp`|`bool`|Whether to round the amount up or down| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|uint256 Amount of currency0 required to cover a position of size liquidity between the two passed prices| ### absDiff Equivalent to: `a >= b ? a - b : b - a` ```solidity function absDiff(uint160 a, uint160 b) internal pure returns (uint256 res); ``` ### getAmount1Delta Gets the amount1 delta between two prices *Calculates liquidity * (sqrt(upper) - sqrt(lower))* ```solidity function getAmount1Delta(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint128 liquidity, bool roundUp) internal pure returns (uint256 amount1); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceAX96`|`uint160`|A sqrt price| |`sqrtPriceBX96`|`uint160`|Another sqrt price| |`liquidity`|`uint128`|The amount of usable liquidity| |`roundUp`|`bool`|Whether to round the amount up, or down| **Returns** |Name|Type|Description| |----|----|-----------| |`amount1`|`uint256`|Amount of currency1 required to cover a position of size liquidity between the two passed prices| ### getAmount0Delta Equivalent to: amount1 = roundUp ? FullMath.mulDivRoundingUp(liquidity, sqrtPriceBX96 - sqrtPriceAX96, FixedPoint96.Q96) : FullMath.mulDiv(liquidity, sqrtPriceBX96 - sqrtPriceAX96, FixedPoint96.Q96); Cannot overflow because `type(uint128).max * type(uint160).max >> 96 < (1 << 192)`. Helper that gets signed currency0 delta ```solidity function getAmount0Delta(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, int128 liquidity) internal pure returns (int256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceAX96`|`uint160`|A sqrt price| |`sqrtPriceBX96`|`uint160`|Another sqrt price| |`liquidity`|`int128`|The change in liquidity for which to compute the amount0 delta| **Returns** |Name|Type|Description| |----|----|-----------| |``|`int256`|int256 Amount of currency0 corresponding to the passed liquidityDelta between the two prices| ### getAmount1Delta Helper that gets signed currency1 delta ```solidity function getAmount1Delta(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, int128 liquidity) internal pure returns (int256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceAX96`|`uint160`|A sqrt price| |`sqrtPriceBX96`|`uint160`|Another sqrt price| |`liquidity`|`int128`|The change in liquidity for which to compute the amount1 delta| **Returns** |Name|Type|Description| |----|----|-----------| |``|`int256`|int256 Amount of currency1 corresponding to the passed liquidityDelta between the two prices| ## Errors ### InvalidPriceOrLiquidity ```solidity error InvalidPriceOrLiquidity(); ``` ### InvalidPrice ```solidity error InvalidPrice(); ``` ### NotEnoughLiquidity ```solidity error NotEnoughLiquidity(); ``` ### PriceOverflow ```solidity error PriceOverflow(); ``` --- ## StateLibrary [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/StateLibrary.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) A helper library to provide state getters that use extsload ## State Variables ### POOLS_SLOT index of pools mapping in the PoolManager ```solidity bytes32 public constant POOLS_SLOT = bytes32(uint256(6)); ``` ### FEE_GROWTH_GLOBAL0_OFFSET index of feeGrowthGlobal0X128 in Pool.State ```solidity uint256 public constant FEE_GROWTH_GLOBAL0_OFFSET = 1; ``` ### LIQUIDITY_OFFSET index of liquidity in Pool.State ```solidity uint256 public constant LIQUIDITY_OFFSET = 3; ``` ### TICKS_OFFSET index of TicksInfo mapping in Pool.State: mapping(int24 => TickInfo) ticks; ```solidity uint256 public constant TICKS_OFFSET = 4; ``` ### TICK_BITMAP_OFFSET index of tickBitmap mapping in Pool.State ```solidity uint256 public constant TICK_BITMAP_OFFSET = 5; ``` ### POSITIONS_OFFSET index of Position.State mapping in Pool.State: mapping(bytes32 => Position.State) positions; ```solidity uint256 public constant POSITIONS_OFFSET = 6; ``` ## Functions ### getSlot0 Get Slot0 of the pool: sqrtPriceX96, tick, protocolFee, lpFee *Corresponds to pools[poolId].slot0* ```solidity function getSlot0(IPoolManager manager, PoolId poolId) internal view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`sqrtPriceX96`|`uint160`|The square root of the price of the pool, in Q96 precision.| |`tick`|`int24`|The current tick of the pool.| |`protocolFee`|`uint24`|The protocol fee of the pool.| |`lpFee`|`uint24`|The swap fee of the pool.| ### getTickInfo Retrieves the tick information of a pool at a specific tick. *Corresponds to pools[poolId].ticks[tick]* ```solidity function getTickInfo(IPoolManager manager, PoolId poolId, int24 tick) internal view returns (uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int24`|The tick to retrieve information for.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidityGross`|`uint128`|The total position liquidity that references this tick| |`liquidityNet`|`int128`|The amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)| |`feeGrowthOutside0X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| |`feeGrowthOutside1X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| ### getTickLiquidity Retrieves the liquidity information of a pool at a specific tick. *Corresponds to pools[poolId].ticks[tick].liquidityGross and pools[poolId].ticks[tick].liquidityNet. A more gas efficient version of getTickInfo* ```solidity function getTickLiquidity(IPoolManager manager, PoolId poolId, int24 tick) internal view returns (uint128 liquidityGross, int128 liquidityNet); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int24`|The tick to retrieve liquidity for.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidityGross`|`uint128`|The total position liquidity that references this tick| |`liquidityNet`|`int128`|The amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)| ### getTickFeeGrowthOutside Retrieves the fee growth outside a tick range of a pool *Corresponds to pools[poolId].ticks[tick].feeGrowthOutside0X128 and pools[poolId].ticks[tick].feeGrowthOutside1X128. A more gas efficient version of getTickInfo* ```solidity function getTickFeeGrowthOutside(IPoolManager manager, PoolId poolId, int24 tick) internal view returns (uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int24`|The tick to retrieve fee growth for.| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthOutside0X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| |`feeGrowthOutside1X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| ### getFeeGrowthGlobals Retrieves the global fee growth of a pool. *Corresponds to pools[poolId].feeGrowthGlobal0X128 and pools[poolId].feeGrowthGlobal1X128* *Note that feeGrowthGlobal can be artificially inflated For pools with a single liquidity position, actors can donate to themselves to freely inflate feeGrowthGlobal atomically donating and collecting fees in the same unlockCallback may make the inflated value more extreme* ```solidity function getFeeGrowthGlobals(IPoolManager manager, PoolId poolId) internal view returns (uint256 feeGrowthGlobal0, uint256 feeGrowthGlobal1); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthGlobal0`|`uint256`|The global fee growth for token0.| |`feeGrowthGlobal1`|`uint256`|The global fee growth for token1.| ### getLiquidity Retrieves total the liquidity of a pool. *Corresponds to pools[poolId].liquidity* ```solidity function getLiquidity(IPoolManager manager, PoolId poolId) internal view returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the pool.| ### getTickBitmap Retrieves the tick bitmap of a pool at a specific tick. *Corresponds to pools[poolId].tickBitmap[tick]* ```solidity function getTickBitmap(IPoolManager manager, PoolId poolId, int16 tick) internal view returns (uint256 tickBitmap); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int16`|The tick to retrieve the bitmap for.| **Returns** |Name|Type|Description| |----|----|-----------| |`tickBitmap`|`uint256`|The bitmap of the tick.| ### getPositionInfo Retrieves the position information of a pool without needing to calculate the `positionId`. *Corresponds to pools[poolId].positions[positionId]* ```solidity function getPositionInfo( IPoolManager manager, PoolId poolId, address owner, int24 tickLower, int24 tickUpper, bytes32 salt ) internal view returns (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|| |`poolId`|`PoolId`|The ID of the pool.| |`owner`|`address`|The owner of the liquidity position.| |`tickLower`|`int24`|The lower tick of the liquidity range.| |`tickUpper`|`int24`|The upper tick of the liquidity range.| |`salt`|`bytes32`|The bytes32 randomness to further distinguish position state.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the position.| |`feeGrowthInside0LastX128`|`uint256`|The fee growth inside the position for token0.| |`feeGrowthInside1LastX128`|`uint256`|The fee growth inside the position for token1.| ### getPositionInfo Retrieves the position information of a pool at a specific position ID. *Corresponds to pools[poolId].positions[positionId]* ```solidity function getPositionInfo(IPoolManager manager, PoolId poolId, bytes32 positionId) internal view returns (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| |`positionId`|`bytes32`|The ID of the position.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the position.| |`feeGrowthInside0LastX128`|`uint256`|The fee growth inside the position for token0.| |`feeGrowthInside1LastX128`|`uint256`|The fee growth inside the position for token1.| ### getPositionLiquidity Retrieves the liquidity of a position. *Corresponds to pools[poolId].positions[positionId].liquidity. More gas efficient for just retrieiving liquidity as compared to getPositionInfo* ```solidity function getPositionLiquidity(IPoolManager manager, PoolId poolId, bytes32 positionId) internal view returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| |`positionId`|`bytes32`|The ID of the position.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the position.| ### getFeeGrowthInside Calculate the fee growth inside a tick range of a pool *pools[poolId].feeGrowthInside0LastX128 in Position.State is cached and can become stale. This function will calculate the up to date feeGrowthInside* ```solidity function getFeeGrowthInside(IPoolManager manager, PoolId poolId, int24 tickLower, int24 tickUpper) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| |`poolId`|`PoolId`|The ID of the pool.| |`tickLower`|`int24`|The lower tick of the range.| |`tickUpper`|`int24`|The upper tick of the range.| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthInside0X128`|`uint256`|The fee growth inside the tick range for token0.| |`feeGrowthInside1X128`|`uint256`|The fee growth inside the tick range for token1.| ### _getPoolStateSlot ```solidity function _getPoolStateSlot(PoolId poolId) internal pure returns (bytes32); ``` ### _getTickInfoSlot ```solidity function _getTickInfoSlot(PoolId poolId, int24 tick) internal pure returns (bytes32); ``` ### _getPositionInfoSlot ```solidity function _getPositionInfoSlot(PoolId poolId, bytes32 positionId) internal pure returns (bytes32); ``` --- ## SwapMath [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/SwapMath.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick. ## State Variables ### MAX_SWAP_FEE the swap fee is represented in hundredths of a bip, so the max is 100% *the swap fee is the total fee on a swap, including both LP and Protocol fee* ```solidity uint256 internal constant MAX_SWAP_FEE = 1e6; ``` ## Functions ### getSqrtPriceTarget Computes the sqrt price target for the next swap step ```solidity function getSqrtPriceTarget(bool zeroForOne, uint160 sqrtPriceNextX96, uint160 sqrtPriceLimitX96) internal pure returns (uint160 sqrtPriceTargetX96); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`zeroForOne`|`bool`|The direction of the swap, true for currency0 to currency1, false for currency1 to currency0| |`sqrtPriceNextX96`|`uint160`|The Q64.96 sqrt price for the next initialized tick| |`sqrtPriceLimitX96`|`uint160`|The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this value after the swap. If one for zero, the price cannot be greater than this value after the swap| **Returns** |Name|Type|Description| |----|----|-----------| |`sqrtPriceTargetX96`|`uint160`|The price target for the next swap step| ### computeSwapStep Computes the result of swapping some amount in, or amount out, given the parameters of the swap *If the swap's amountSpecified is negative, the combined fee and input amount will never exceed the absolute value of the remaining amount.* *feePips must be no larger than MAX_SWAP_FEE for this function. We ensure that before setting a fee using LPFeeLibrary.isValid.* ```solidity function computeSwapStep( uint160 sqrtPriceCurrentX96, uint160 sqrtPriceTargetX96, uint128 liquidity, int256 amountRemaining, uint24 feePips ) internal pure returns (uint160 sqrtPriceNextX96, uint256 amountIn, uint256 amountOut, uint256 feeAmount); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceCurrentX96`|`uint160`|The current sqrt price of the pool| |`sqrtPriceTargetX96`|`uint160`|The price that cannot be exceeded, from which the direction of the swap is inferred| |`liquidity`|`uint128`|The usable liquidity| |`amountRemaining`|`int256`|How much input or output amount is remaining to be swapped in/out| |`feePips`|`uint24`|The fee taken from the input amount, expressed in hundredths of a bip| **Returns** |Name|Type|Description| |----|----|-----------| |`sqrtPriceNextX96`|`uint160`|The price after swapping the amount in/out, not to exceed the price target| |`amountIn`|`uint256`|The amount to be swapped in, of either currency0 or currency1, based on the direction of the swap| |`amountOut`|`uint256`|The amount to be received, of either currency0 or currency1, based on the direction of the swap| |`feeAmount`|`uint256`|The amount of input that will be taken as a fee| --- ## TickBitmap [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/TickBitmap.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Stores a packed mapping of tick index to its initialized state *The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.* ## Functions ### compress *round towards negative infinity* ```solidity function compress(int24 tick, int24 tickSpacing) internal pure returns (int24 compressed); ``` ### position Computes the position in the mapping where the initialized bit for a tick lives ```solidity function position(int24 tick) internal pure returns (int16 wordPos, uint8 bitPos); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tick`|`int24`|The tick for which to compute the position| **Returns** |Name|Type|Description| |----|----|-----------| |`wordPos`|`int16`|The key in the mapping containing the word in which the bit is stored| |`bitPos`|`uint8`|The bit position in the word where the flag is stored| ### flipTick Flips the initialized state for a given tick from false to true, or vice versa ```solidity function flipTick(mapping(int16 => uint256) storage self, int24 tick, int24 tickSpacing) internal; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`mapping(int16 => uint256)`|The mapping in which to flip the tick| |`tick`|`int24`|The tick to flip| |`tickSpacing`|`int24`|The spacing between usable ticks| ### nextInitializedTickWithinOneWord Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either to the left (less than or equal to) or right (greater than) of the given tick ```solidity function nextInitializedTickWithinOneWord( mapping(int16 => uint256) storage self, int24 tick, int24 tickSpacing, bool lte ) internal view returns (int24 next, bool initialized); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`self`|`mapping(int16 => uint256)`|The mapping in which to compute the next initialized tick| |`tick`|`int24`|The starting tick| |`tickSpacing`|`int24`|The spacing between usable ticks| |`lte`|`bool`|Whether to search for the next initialized tick to the left (less than or equal to the starting tick)| **Returns** |Name|Type|Description| |----|----|-----------| |`next`|`int24`|The next initialized or uninitialized tick up to 256 ticks away from the current tick| |`initialized`|`bool`|Whether the next tick is initialized, as the function only searches within up to 256 ticks| ## Errors ### TickMisaligned Thrown when the tick is not enumerated by the tick spacing ```solidity error TickMisaligned(int24 tick, int24 tickSpacing); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tick`|`int24`|the invalid tick| |`tickSpacing`|`int24`|The tick spacing of the pool| --- ## TickMath [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/TickMath.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports prices between 2**-128 and 2**128 ## State Variables ### MIN_TICK *The minimum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**-128* *If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used* ```solidity int24 internal constant MIN_TICK = -887272; ``` ### MAX_TICK *The maximum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**128* *If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used* ```solidity int24 internal constant MAX_TICK = 887272; ``` ### MIN_TICK_SPACING *The minimum tick spacing value drawn from the range of type int16 that is greater than 0, i.e. min from the range [1, 32767]* ```solidity int24 internal constant MIN_TICK_SPACING = 1; ``` ### MAX_TICK_SPACING *The maximum tick spacing value drawn from the range of type int16, i.e. max from the range [1, 32767]* ```solidity int24 internal constant MAX_TICK_SPACING = type(int16).max; ``` ### MIN_SQRT_PRICE *The minimum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MIN_TICK)* ```solidity uint160 internal constant MIN_SQRT_PRICE = 4295128739; ``` ### MAX_SQRT_PRICE *The maximum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MAX_TICK)* ```solidity uint160 internal constant MAX_SQRT_PRICE = 1461446703485210103287273052203988822378723970342; ``` ### MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE *A threshold used for optimized bounds check, equals `MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1`* ```solidity uint160 internal constant MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE = 1461446703485210103287273052203988822378723970342 - 4295128739 - 1; ``` ## Functions ### maxUsableTick Given a tickSpacing, compute the maximum usable tick ```solidity function maxUsableTick(int24 tickSpacing) internal pure returns (int24); ``` ### minUsableTick Given a tickSpacing, compute the minimum usable tick ```solidity function minUsableTick(int24 tickSpacing) internal pure returns (int24); ``` ### getSqrtPriceAtTick Calculates sqrt(1.0001^tick) * 2^96 *Throws if |tick| > max tick* ```solidity function getSqrtPriceAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tick`|`int24`|The input tick for the above formula| **Returns** |Name|Type|Description| |----|----|-----------| |`sqrtPriceX96`|`uint160`|A Fixed point Q64.96 number representing the sqrt of the price of the two assets (currency1/currency0) at the given tick| ### getTickAtSqrtPrice Calculates the greatest tick value such that getSqrtPriceAtTick(tick) \<= sqrtPriceX96 *Throws in case sqrtPriceX96 < MIN_SQRT_PRICE, as MIN_SQRT_PRICE is the lowest value getSqrtPriceAtTick may ever return.* ```solidity function getTickAtSqrtPrice(uint160 sqrtPriceX96) internal pure returns (int24 tick); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceX96`|`uint160`|The sqrt price for which to compute the tick as a Q64.96| **Returns** |Name|Type|Description| |----|----|-----------| |`tick`|`int24`|The greatest tick for which the getSqrtPriceAtTick(tick) is less than or equal to the input sqrtPriceX96| ## Errors ### InvalidTick Thrown when the tick passed to #getSqrtPriceAtTick is not between MIN_TICK and MAX_TICK ```solidity error InvalidTick(int24 tick); ``` ### InvalidSqrtPrice Thrown when the price passed to #getTickAtSqrtPrice does not correspond to a price between MIN_TICK and MAX_TICK ```solidity error InvalidSqrtPrice(uint160 sqrtPriceX96); ``` --- ## TransientStateLibrary [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/TransientStateLibrary.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) A helper library to provide state getters that use exttload ## Functions ### getSyncedReserves returns the reserves for the synced currency *returns 0 if the reserves are not synced or value is 0. Checks the synced currency to only return valid reserve values (after a sync and before a settle).* ```solidity function getSyncedReserves(IPoolManager manager) internal view returns (uint256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|The pool manager contract.| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|uint256 The reserves of the currency.| ### getSyncedCurrency ```solidity function getSyncedCurrency(IPoolManager manager) internal view returns (Currency); ``` ### getNonzeroDeltaCount Returns the number of nonzero deltas open on the PoolManager that must be zeroed out before the contract is locked ```solidity function getNonzeroDeltaCount(IPoolManager manager) internal view returns (uint256); ``` ### currencyDelta Get the current delta for a caller in the given currency ```solidity function currencyDelta(IPoolManager manager, address target, Currency currency) internal view returns (int256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`manager`|`IPoolManager`|| |`target`|`address`|The credited account address| |`currency`|`Currency`|The currency for which to lookup the delta| ### isUnlocked Returns whether the contract is unlocked or not ```solidity function isUnlocked(IPoolManager manager) internal view returns (bool); ``` --- ## UnsafeMath [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/UnsafeMath.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Contains methods that perform common math functions but do not do any overflow or underflow checks ## Functions ### divRoundingUp Returns ceil(x / y) *division by 0 will return 0, and should be checked externally* ```solidity function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`uint256`|The dividend| |`y`|`uint256`|The divisor| **Returns** |Name|Type|Description| |----|----|-----------| |`z`|`uint256`|The quotient, ceil(x / y)| ### simpleMulDiv Calculates floor(a×b÷denominator) *division by 0 will return 0, and should be checked externally* ```solidity function simpleMulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`a`|`uint256`|The multiplicand| |`b`|`uint256`|The multiplier| |`denominator`|`uint256`|The divisor| **Returns** |Name|Type|Description| |----|----|-----------| |`result`|`uint256`|The 256-bit result, floor(a×b÷denominator)| --- ## LiquidityAmounts The `LiquidityAmounts` library provides functions for computing liquidity amounts from token amounts and prices in Uniswap V4. ## Key Concept: sqrtPriceX96 `sqrtPriceX96` represents the square root of the price ratio of token1 to token0, multiplied by 2^96. This representation allows for precise price calculations across a wide range of values while using fixed-point arithmetic. It's more efficient than using ticks for intermediate calculations, as it avoids frequent conversions between prices and ticks. ## Functions ### getLiquidityForAmount0 ```solidity function getLiquidityForAmount0(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint256 amount0) internal pure returns (uint128 liquidity) ``` Computes the amount of liquidity received for a given amount of token0 and price range. | Param Name | Type | Description | |----------------|---------|-----------------------------------------------| | sqrtPriceAX96 | uint160 | Square root of price at first tick boundary | | sqrtPriceBX96 | uint160 | Square root of price at second tick boundary | | amount0 | uint256 | The amount of token0 being sent in | Returns the amount of liquidity received. ### getLiquidityForAmount1 ```solidity function getLiquidityForAmount1(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint256 amount1) internal pure returns (uint128 liquidity) ``` Computes the amount of liquidity received for a given amount of token1 and price range. | Param Name | Type | Description | |----------------|---------|-----------------------------------------------| | sqrtPriceAX96 | uint160 | Square root of price at first tick boundary | | sqrtPriceBX96 | uint160 | Square root of price at second tick boundary | | amount1 | uint256 | The amount of token1 being sent in | Returns the amount of liquidity received. ### getLiquidityForAmounts ```solidity function getLiquidityForAmounts( uint160 sqrtPriceX96, uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint256 amount0, uint256 amount1 ) internal pure returns (uint128 liquidity) ``` Computes the maximum amount of liquidity received for given amounts of token0 and token1, the current pool prices, and the prices at the tick boundaries. | Param Name | Type | Description | |----------------|---------|-----------------------------------------------| | sqrtPriceX96 | uint160 | Current square root price of the pool | | sqrtPriceAX96 | uint160 | Square root of price at first tick boundary | | sqrtPriceBX96 | uint160 | Square root of price at second tick boundary | | amount0 | uint256 | The amount of token0 being sent in | | amount1 | uint256 | The amount of token1 being sent in | Returns the maximum amount of liquidity received. ### getAmount0ForLiquidity ```solidity function getAmount0ForLiquidity(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint128 liquidity) internal pure returns (uint256 amount0) ``` Computes the amount of token0 for a given amount of liquidity and a price range. | Param Name | Type | Description | |----------------|---------|-----------------------------------------------| | sqrtPriceAX96 | uint160 | Square root of price at first tick boundary | | sqrtPriceBX96 | uint160 | Square root of price at second tick boundary | | liquidity | uint128 | The liquidity being valued | Returns the amount of token0. ### getAmount1ForLiquidity ```solidity function getAmount1ForLiquidity(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint128 liquidity) internal pure returns (uint256 amount1) ``` Computes the amount of token1 for a given amount of liquidity and a price range. | Param Name | Type | Description | |----------------|---------|-----------------------------------------------| | sqrtPriceAX96 | uint160 | Square root of price at first tick boundary | | sqrtPriceBX96 | uint160 | Square root of price at second tick boundary | | liquidity | uint128 | The liquidity being valued | Returns the amount of token1. ### getAmountsForLiquidity ```solidity function getAmountsForLiquidity( uint160 sqrtPriceX96, uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint128 liquidity ) internal pure returns (uint256 amount0, uint256 amount1) ``` Computes the token0 and token1 value for a given amount of liquidity, the current pool prices, and the prices at the tick boundaries. | Param Name | Type | Description | |----------------|---------|-----------------------------------------------| | sqrtPriceX96 | uint160 | Current square root price of the pool | | sqrtPriceAX96 | uint160 | Square root of price at first tick boundary | | sqrtPriceBX96 | uint160 | Square root of price at second tick boundary | | liquidity | uint128 | The liquidity being valued | Returns the amount of token0 and token1. --- ## TransientStateLibrary(Libraries) The `TransientStateLibrary` is a crucial component of Uniswap V4, providing utility functions for managing transient state in the PoolManager contract. This library handles operations related to reserves, delta counts, and locking state, which are essential for the efficient and secure operation of the Uniswap V4 protocol. ## Key Concepts ### Transient Storage Uniswap V4 uses transient storage to optimize gas costs and improve efficiency. Transient storage, introduced in EIP-1153, is a way to store data that is only needed for the duration of a transaction, without persisting it to the blockchain's state trie. This is achieved using the `TLOAD` and `TSTORE` opcodes. Key points about transient storage in Uniswap V4: 1. **PoolManager and** `exttload`: Instead of exposing custom getters for transient storage, the PoolManager implements an `exttload` (external tload) function. This function serves as an external wrapper for the `TLOAD` opcode, providing a standardized interface for accessing transient storage. 2. **TransientStateLibrary's Role:** The `TransientStateLibrary` acts as an intermediary, making calls to the PoolManager's `exttload` function to access transient storage. This abstraction simplifies the interaction with transient storage for other parts of the Uniswap V4 ecosystem. 3. **Standardization:** By channeling all transient storage access through the PoolManager's `exttload` function, Uniswap V4 ensures a consistent and controlled approach to managing transient data across the protocol. Common operations that involve transient state include: - Checking reserves (`getReserves`) - Verifying currency deltas (`currencyDelta`) - Syncing currency states (`sync`) - Settling currency balances (`settle`) This architecture allows Uniswap V4 to benefit from the gas efficiency of transient storage while maintaining a clean and standardized interface for interacting with this temporary data. ## Functions ### getReserves ```solidity function getReserves(IPoolManager manager, Currency currency) internal view returns (uint256) ``` Retrieves the reserves of a specific currency from the PoolManager's transient storage. | Param Name | Type | Description | |------------|--------------|--------------------------------------------------| | manager | IPoolManager | The PoolManager contract instance | | currency | Currency | The currency for which to fetch reserves | **Returns:** - `uint256`: The amount of reserves for the specified currency **Notes:** - Returns `0` if the reserves are not synced - Returns `type(uint256).max` if the reserves are synced but the value is `0` ### getNonzeroDeltaCount ```solidity function getNonzeroDeltaCount(IPoolManager manager) internal view returns (uint256) ``` Retrieves the count of nonzero deltas that must be zeroed out before the contract can be locked. | Param Name | Type | Description | |------------|--------------|--------------------------------------| | manager | IPoolManager | The PoolManager contract instance | **Returns:** - `uint256`: The number of nonzero deltas ### currencyDelta ```solidity function currencyDelta(IPoolManager manager, address caller_, Currency currency) internal view returns (int256) ``` Fetches the current delta for a specific caller and currency from the PoolManager's transient storage. | Param Name | Type | Description | |------------|--------------|--------------------------------------------------| | manager | IPoolManager | The PoolManager contract instance | | caller_ | address | The address of the caller | | currency | Currency | The currency for which to lookup the delta | **Returns:** - `int256`: The delta value for the specified caller and currency **Notes:** - A **negative** delta indicates an amount that must be **paid or settled** by the caller. In other words, a negative delta means the caller owes that amount and needs to pay or settle it. - A **positive** delta indicates an amount that is owed to the caller. This delta amount must be **taken or claimed** by the caller. ### isUnlocked ```solidity function isUnlocked(IPoolManager manager) internal view returns (bool) ``` Checks if the PoolManager contract is currently unlocked. | Param Name | Type | Description | |------------|--------------|--------------------------------------| | manager | IPoolManager | The PoolManager contract instance | **Returns:** - `bool`: `true` if the contract is unlocked, `false` otherwise ## Usage and Importance The `TransientStateLibrary` plays a critical role in Uniswap V4's operation: 1. **Gas Optimization:** By using transient storage, the library helps reduce gas costs associated with state changes that are only relevant within a single transaction. This is particularly important for multi-hop transactions, where internal net balances (deltas) are updated instead of making token transfers for each hop. 2. **Security:** The library provides functions to check the lock state and manage deltas, which are crucial for maintaining the integrity of the protocol during operations. The use of transient storage also allows for more efficient implementation of security measures compared to V3's reentrancy guards. 3. **Flexibility:** The library allows for efficient management of currency-specific data, such as reserves and deltas, which is essential for Uniswap V4's multi-currency pools. 4. **Encapsulation:** By centralizing these utility functions in a library, the code promotes better organization and reusability across the Uniswap V4 codebase. ## Integration with PoolManager The `TransientStateLibrary` is designed to work closely with the `PoolManager` contract. The `TransientStateLibrary` can be easily integrated with the `PoolManager` contract using the `using` keyword for syntactic sugar. This allows you to call the library functions as if they were methods of the `IPoolManager` instance. Here's an example: ```solidity contract Example { using TransientStateLibrary for IPoolManager; function example() external { int256 delta = manager.currencyDelta(address(this), currency); // Use the delta value... } } ``` In this example, the `using TransientStateLibrary for IPoolManager;` statement allows you to call `currencyDelta` directly on the `manager` instance, making your code more readable and concise. --- ## ActionsRouter [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/ActionsRouter.sol) **Inherits:** [IUnlockCallback](/src/interfaces/callback/IUnlockCallback.sol/interface.IUnlockCallback.md), Test A router that handles an arbitrary input of actions. TODO: Can continue to add functions per action. ## Actions [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/ActionsRouter.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ```solidity enum Actions { SETTLE, SETTLE_NATIVE, SETTLE_FOR, TAKE, PRANK_TAKE_FROM, SYNC, MINT, CLEAR, ASSERT_BALANCE_EQUALS, ASSERT_RESERVES_EQUALS, ASSERT_DELTA_EQUALS, ASSERT_NONZERO_DELTA_COUNT_EQUALS, TRANSFER_FROM, COLLECT_PROTOCOL_FEES } ``` ## State Variables ### manager ```solidity IPoolManager manager; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _manager); ``` ### unlockCallback ```solidity function unlockCallback(bytes calldata data) external returns (bytes memory); ``` ### executeActions ```solidity function executeActions(Actions[] memory actions, bytes[] memory params) external payable; ``` ### _settle ```solidity function _settle() internal; ``` ### _settleNative ```solidity function _settleNative(bytes memory params) internal; ``` ### _settleFor ```solidity function _settleFor(bytes memory params) internal; ``` ### _take ```solidity function _take(bytes memory params) internal; ``` ### _prankTakeFrom ```solidity function _prankTakeFrom(bytes memory params) internal; ``` ### _sync ```solidity function _sync(bytes memory params) internal; ``` ### _mint ```solidity function _mint(bytes memory params) internal; ``` ### _clear ```solidity function _clear(bytes memory params) internal; ``` ### _assertBalanceEquals ```solidity function _assertBalanceEquals(bytes memory params) internal view; ``` ### _assertReservesEquals ```solidity function _assertReservesEquals(bytes memory params) internal view; ``` ### _assertDeltaEquals ```solidity function _assertDeltaEquals(bytes memory params) internal view; ``` ### _assertNonzeroDeltaCountEquals ```solidity function _assertNonzeroDeltaCountEquals(bytes memory params) internal view; ``` ### _transferFrom ```solidity function _transferFrom(bytes memory params) internal; ``` ### _collectProtocolFees ```solidity function _collectProtocolFees(bytes memory params) internal; ``` ## Errors ### ActionNotSupported ```solidity error ActionNotSupported(); ``` ### CheckParameters ```solidity error CheckParameters(); ``` --- ## BaseTestHooks [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/BaseTestHooks.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IHooks](contracts/v4/reference/core/interfaces/IHooks.md) ## Functions ### beforeInitialize ```solidity function beforeInitialize(address, PoolKey calldata, uint160) external virtual returns (bytes4); ``` ### afterInitialize ```solidity function afterInitialize(address, PoolKey calldata, uint160, int24) external virtual returns (bytes4); ``` ### beforeAddLiquidity ```solidity function beforeAddLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata) external virtual returns (bytes4); ``` ### afterAddLiquidity ```solidity function afterAddLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata ) external virtual returns (bytes4, BalanceDelta); ``` ### beforeRemoveLiquidity ```solidity function beforeRemoveLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata) external virtual returns (bytes4); ``` ### afterRemoveLiquidity ```solidity function afterRemoveLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata ) external virtual returns (bytes4, BalanceDelta); ``` ### beforeSwap ```solidity function beforeSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata) external virtual returns (bytes4, BeforeSwapDelta, uint24); ``` ### afterSwap ```solidity function afterSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata) external virtual returns (bytes4, int128); ``` ### beforeDonate ```solidity function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external virtual returns (bytes4); ``` ### afterDonate ```solidity function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external virtual returns (bytes4); ``` ## Errors ### HookNotImplemented ```solidity error HookNotImplemented(); ``` --- ## CurrencyTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/CurrencyTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## Functions ### transfer ```solidity function transfer(Currency currency, address to, uint256 amount) external; ``` ### balanceOfSelf ```solidity function balanceOfSelf(Currency currency) external view returns (uint256); ``` ### balanceOf ```solidity function balanceOf(Currency currency, address owner) external view returns (uint256); ``` ### isAddressZero ```solidity function isAddressZero(Currency currency) external pure returns (bool); ``` ### toId ```solidity function toId(Currency currency) external pure returns (uint256); ``` ### fromId ```solidity function fromId(uint256 id) external pure returns (Currency); ``` --- ## CustomCurveHook [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/CustomCurveHook.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [BaseTestHooks](contracts/v4/reference/core/test/BaseTestHooks.md) ## State Variables ### manager ```solidity IPoolManager immutable manager; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _manager); ``` ### onlyPoolManager ```solidity modifier onlyPoolManager(); ``` ### beforeSwap ```solidity function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata) external override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24); ``` ### afterAddLiquidity ```solidity function afterAddLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata ) external view override onlyPoolManager returns (bytes4, BalanceDelta); ``` ### _getInputOutputAndAmount ```solidity function _getInputOutputAndAmount(PoolKey calldata key, IPoolManager.SwapParams calldata params) internal pure returns (Currency input, Currency output, uint256 amount); ``` ## Errors ### AddLiquidityDirectToHook ```solidity error AddLiquidityDirectToHook(); ``` --- ## DeltaReturningHook [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/DeltaReturningHook.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [BaseTestHooks](contracts/v4/reference/core/test/BaseTestHooks.md) ## State Variables ### manager ```solidity IPoolManager immutable manager; ``` ### deltaSpecified ```solidity int128 deltaSpecified; ``` ### deltaUnspecifiedBeforeSwap ```solidity int128 deltaUnspecifiedBeforeSwap; ``` ### deltaUnspecifiedAfterSwap ```solidity int128 deltaUnspecifiedAfterSwap; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _manager); ``` ### onlyPoolManager ```solidity modifier onlyPoolManager(); ``` ### setDeltaSpecified ```solidity function setDeltaSpecified(int128 delta) external; ``` ### setDeltaUnspecifiedBeforeSwap ```solidity function setDeltaUnspecifiedBeforeSwap(int128 delta) external; ``` ### setDeltaUnspecifiedAfterSwap ```solidity function setDeltaUnspecifiedAfterSwap(int128 delta) external; ``` ### beforeSwap ```solidity function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata) external override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24); ``` ### afterSwap ```solidity function afterSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, BalanceDelta, bytes calldata) external override onlyPoolManager returns (bytes4, int128); ``` ### _sortCurrencies ```solidity function _sortCurrencies(PoolKey calldata key, IPoolManager.SwapParams calldata params) internal pure returns (Currency specified, Currency unspecified); ``` ### _settleOrTake ```solidity function _settleOrTake(Currency currency, int128 delta) internal; ``` --- ## DynamicFeesTestHook [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/DynamicFeesTestHook.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [BaseTestHooks](contracts/v4/reference/core/test/BaseTestHooks.md) ## State Variables ### fee ```solidity uint24 internal fee; ``` ### manager ```solidity IPoolManager manager; ``` ## Functions ### setManager ```solidity function setManager(IPoolManager _manager) external; ``` ### setFee ```solidity function setFee(uint24 _fee) external; ``` ### afterInitialize ```solidity function afterInitialize(address, PoolKey calldata key, uint160, int24) external override returns (bytes4); ``` ### beforeSwap ```solidity function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata) external override returns (bytes4, BeforeSwapDelta, uint24); ``` ### forcePoolFeeUpdate ```solidity function forcePoolFeeUpdate(PoolKey calldata _key, uint24 _fee) external; ``` --- ## DynamicReturnFeeTestHook [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/DynamicReturnFeeTestHook.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [BaseTestHooks](contracts/v4/reference/core/test/BaseTestHooks.md) ## State Variables ### fee ```solidity uint24 internal fee; ``` ### manager ```solidity IPoolManager manager; ``` ## Functions ### setManager ```solidity function setManager(IPoolManager _manager) external; ``` ### setFee ```solidity function setFee(uint24 _fee) external; ``` ### beforeSwap ```solidity function beforeSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata) external view override returns (bytes4, BeforeSwapDelta, uint24); ``` ### forcePoolFeeUpdate ```solidity function forcePoolFeeUpdate(PoolKey calldata _key, uint24 _fee) external; ``` --- ## EmptyRevertContract [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/EmptyRevertContract.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## Functions ### fallback ```solidity fallback() external; ``` --- ## EmptyTestHooks [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/EmptyTestHooks.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IHooks](contracts/v4/reference/core/interfaces/IHooks.md) ## Functions ### constructor ```solidity constructor(); ``` ### beforeInitialize ```solidity function beforeInitialize(address, PoolKey calldata, uint160) external pure override returns (bytes4); ``` ### afterInitialize ```solidity function afterInitialize(address, PoolKey calldata, uint160, int24) external pure override returns (bytes4); ``` ### beforeAddLiquidity ```solidity function beforeAddLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata) external pure override returns (bytes4); ``` ### afterAddLiquidity ```solidity function afterAddLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata ) external pure override returns (bytes4, BalanceDelta); ``` ### beforeRemoveLiquidity ```solidity function beforeRemoveLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata) external pure override returns (bytes4); ``` ### afterRemoveLiquidity ```solidity function afterRemoveLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata ) external pure override returns (bytes4, BalanceDelta); ``` ### beforeSwap ```solidity function beforeSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata) external pure override returns (bytes4, BeforeSwapDelta, uint24); ``` ### afterSwap ```solidity function afterSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata) external pure override returns (bytes4, int128); ``` ### beforeDonate ```solidity function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external pure override returns (bytes4); ``` ### afterDonate ```solidity function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external pure override returns (bytes4); ``` --- ## FeeTakingHook [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/FeeTakingHook.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [BaseTestHooks](contracts/v4/reference/core/test/BaseTestHooks.md) ## State Variables ### manager ```solidity IPoolManager immutable manager; ``` ### LIQUIDITY_FEE ```solidity uint128 public constant LIQUIDITY_FEE = 543; ``` ### SWAP_FEE_BIPS ```solidity uint128 public constant SWAP_FEE_BIPS = 123; ``` ### TOTAL_BIPS ```solidity uint128 public constant TOTAL_BIPS = 10000; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _manager); ``` ### onlyPoolManager ```solidity modifier onlyPoolManager(); ``` ### afterSwap ```solidity function afterSwap( address, PoolKey calldata key, IPoolManager.SwapParams calldata params, BalanceDelta delta, bytes calldata ) external override onlyPoolManager returns (bytes4, int128); ``` ### afterRemoveLiquidity ```solidity function afterRemoveLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta delta, BalanceDelta, bytes calldata ) external override onlyPoolManager returns (bytes4, BalanceDelta); ``` ### afterAddLiquidity ```solidity function afterAddLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta delta, BalanceDelta, bytes calldata ) external override onlyPoolManager returns (bytes4, BalanceDelta); ``` --- ## Fuzzers [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/Fuzzers.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** StdUtils ## State Variables ### _vm ```solidity Vm internal constant _vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); ``` ## Functions ### boundLiquidityDelta ```solidity function boundLiquidityDelta(PoolKey memory key, int256 liquidityDeltaUnbounded, int256 liquidityMaxByAmount) internal pure returns (int256); ``` ### boundLiquidityDeltaTightly ```solidity function boundLiquidityDeltaTightly( PoolKey memory key, int256 liquidityDeltaUnbounded, int256 liquidityMaxByAmount, uint256 maxPositions ) internal pure returns (int256); ``` ### getLiquidityDeltaFromAmounts ```solidity function getLiquidityDeltaFromAmounts(int24 tickLower, int24 tickUpper, uint160 sqrtPriceX96) internal pure returns (int256); ``` ### boundTicks ```solidity function boundTicks(int24 tickLower, int24 tickUpper, int24 tickSpacing) internal pure returns (int24, int24); ``` ### boundTicks ```solidity function boundTicks(PoolKey memory key, int24 tickLower, int24 tickUpper) internal pure returns (int24, int24); ``` ### createRandomSqrtPriceX96 ```solidity function createRandomSqrtPriceX96(int24 tickSpacing, int256 seed) internal pure returns (uint160); ``` ### createFuzzyLiquidityParams *Obtain fuzzed and bounded parameters for creating liquidity* ```solidity function createFuzzyLiquidityParams( PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, uint160 sqrtPriceX96 ) internal pure returns (IPoolManager.ModifyLiquidityParams memory result); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool key| |`params`|`IPoolManager.ModifyLiquidityParams`|IPoolManager.ModifyLiquidityParams Note that these parameters are unbounded| |`sqrtPriceX96`|`uint160`|The current sqrt price| ### createFuzzyLiquidityParamsWithTightBound ```solidity function createFuzzyLiquidityParamsWithTightBound( PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, uint160 sqrtPriceX96, uint256 maxPositions ) internal pure returns (IPoolManager.ModifyLiquidityParams memory result); ``` ### createFuzzyLiquidity ```solidity function createFuzzyLiquidity( PoolModifyLiquidityTest modifyLiquidityRouter, PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, uint160 sqrtPriceX96, bytes memory hookData ) internal returns (IPoolManager.ModifyLiquidityParams memory result, BalanceDelta delta); ``` ### createFuzzyLiquidityWithTightBound ```solidity function createFuzzyLiquidityWithTightBound( PoolModifyLiquidityTest modifyLiquidityRouter, PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, uint160 sqrtPriceX96, bytes memory hookData, uint256 maxPositions ) internal returns (IPoolManager.ModifyLiquidityParams memory result, BalanceDelta delta); ``` --- ## HooksTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/HooksTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## Functions ### validateHookPermissions ```solidity function validateHookPermissions(address hookAddress, Hooks.Permissions calldata params) external pure; ``` ### isValidHookAddress ```solidity function isValidHookAddress(address hookAddress, uint24 fee) external pure returns (bool); ``` ### shouldCallBeforeInitialize ```solidity function shouldCallBeforeInitialize(address hookAddress) external pure returns (bool); ``` ### shouldCallAfterInitialize ```solidity function shouldCallAfterInitialize(address hookAddress) external pure returns (bool); ``` ### shouldCallBeforeSwap ```solidity function shouldCallBeforeSwap(address hookAddress) external pure returns (bool); ``` ### shouldCallAfterSwap ```solidity function shouldCallAfterSwap(address hookAddress) external pure returns (bool); ``` ### shouldCallBeforeAddLiquidity ```solidity function shouldCallBeforeAddLiquidity(address hookAddress) external pure returns (bool); ``` ### shouldCallAfterAddLiquidity ```solidity function shouldCallAfterAddLiquidity(address hookAddress) external pure returns (bool); ``` ### shouldCallBeforeRemoveLiquidity ```solidity function shouldCallBeforeRemoveLiquidity(address hookAddress) external pure returns (bool); ``` ### shouldCallAfterRemoveLiquidity ```solidity function shouldCallAfterRemoveLiquidity(address hookAddress) external pure returns (bool); ``` ### shouldCallBeforeDonate ```solidity function shouldCallBeforeDonate(address hookAddress) external pure returns (bool); ``` ### shouldCallAfterDonate ```solidity function shouldCallAfterDonate(address hookAddress) external pure returns (bool); ``` ### getGasCostOfShouldCall ```solidity function getGasCostOfShouldCall(address hookAddress) external view returns (uint256); ``` ### getGasCostOfValidateHookAddress ```solidity function getGasCostOfValidateHookAddress(address hookAddress, Hooks.Permissions calldata params) external view returns (uint256); ``` --- ## LPFeeTakingHook [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/LPFeeTakingHook.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [BaseTestHooks](contracts/v4/reference/core/test/BaseTestHooks.md) a hook that takes all of the LP fee revenue *an example test hook to validate the data is provided correctly* ## State Variables ### manager ```solidity IPoolManager immutable manager; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _manager); ``` ### onlyPoolManager ```solidity modifier onlyPoolManager(); ``` ### afterRemoveLiquidity ```solidity function afterRemoveLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta feeDelta, bytes calldata ) external override onlyPoolManager returns (bytes4, BalanceDelta); ``` ### afterAddLiquidity ```solidity function afterAddLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta feeDelta, bytes calldata ) external override onlyPoolManager returns (bytes4, BalanceDelta); ``` --- ## LiquidityMathTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/LiquidityMathTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## Functions ### addDelta ```solidity function addDelta(uint128 x, int128 y) external pure returns (uint128 z); ``` --- ## MockContract [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/MockContract.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** Proxy Mock contract that tracks the number of calls to various functions by selector *allows for proxying to an implementation contract if real logic or return values are needed* ## State Variables ### calls ```solidity mapping(bytes32 => uint256) public calls; ``` ### callParams ```solidity mapping(bytes32 => mapping(bytes => uint256)) public callParams; ``` ### impl If set, delegatecall to implementation after tracking call ```solidity address internal impl; ``` ## Functions ### timesCalledSelector ```solidity function timesCalledSelector(bytes32 selector) public view returns (uint256); ``` ### timesCalled ```solidity function timesCalled(string calldata fnSig) public view returns (uint256); ``` ### calledWithSelector ```solidity function calledWithSelector(bytes32 selector, bytes calldata params) public view returns (bool); ``` ### calledWith ```solidity function calledWith(string calldata fnSig, bytes calldata params) public view returns (bool); ``` ### _implementation exposes implementation contract address ```solidity function _implementation() internal view override returns (address); ``` ### setImplementation ```solidity function setImplementation(address _impl) external; ``` ### _beforeFallback Captures calls by selector ```solidity function _beforeFallback() internal; ``` ### _fallback ```solidity function _fallback() internal override; ``` ### receive ```solidity receive() external payable; ``` --- ## MockERC6909Claims [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/MockERC6909Claims.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [ERC6909Claims](contracts/v4/reference/core/ERC6909Claims.md) Mock contract for testing ERC6909Claims ## Functions ### mint mocked mint logic ```solidity function mint(address to, uint256 id, uint256 amount) public; ``` ### burn mocked burn logic ```solidity function burn(uint256 id, uint256 amount) public; ``` ### burnFrom mocked burn logic without checking sender allowance ```solidity function burnFrom(address from, uint256 id, uint256 amount) public; ``` --- ## MockHooks [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/MockHooks.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IHooks](contracts/v4/reference/core/interfaces/IHooks.md) ## State Variables ### beforeInitializeData ```solidity bytes public beforeInitializeData; ``` ### afterInitializeData ```solidity bytes public afterInitializeData; ``` ### beforeAddLiquidityData ```solidity bytes public beforeAddLiquidityData; ``` ### afterAddLiquidityData ```solidity bytes public afterAddLiquidityData; ``` ### beforeRemoveLiquidityData ```solidity bytes public beforeRemoveLiquidityData; ``` ### afterRemoveLiquidityData ```solidity bytes public afterRemoveLiquidityData; ``` ### beforeSwapData ```solidity bytes public beforeSwapData; ``` ### afterSwapData ```solidity bytes public afterSwapData; ``` ### beforeDonateData ```solidity bytes public beforeDonateData; ``` ### afterDonateData ```solidity bytes public afterDonateData; ``` ### returnValues ```solidity mapping(bytes4 => bytes4) public returnValues; ``` ### lpFees ```solidity mapping(PoolId => uint16) public lpFees; ``` ## Functions ### beforeInitialize ```solidity function beforeInitialize(address, PoolKey calldata, uint160) external override returns (bytes4); ``` ### afterInitialize ```solidity function afterInitialize(address, PoolKey calldata, uint160, int24) external override returns (bytes4); ``` ### beforeAddLiquidity ```solidity function beforeAddLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata hookData ) external override returns (bytes4); ``` ### afterAddLiquidity ```solidity function afterAddLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata hookData ) external override returns (bytes4, BalanceDelta); ``` ### beforeRemoveLiquidity ```solidity function beforeRemoveLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata hookData ) external override returns (bytes4); ``` ### afterRemoveLiquidity ```solidity function afterRemoveLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata hookData ) external override returns (bytes4, BalanceDelta); ``` ### beforeSwap ```solidity function beforeSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata hookData) external override returns (bytes4, BeforeSwapDelta, uint24); ``` ### afterSwap ```solidity function afterSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata hookData) external override returns (bytes4, int128); ``` ### beforeDonate ```solidity function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata hookData) external override returns (bytes4); ``` ### afterDonate ```solidity function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata hookData) external override returns (bytes4); ``` ### setReturnValue ```solidity function setReturnValue(bytes4 key, bytes4 value) external; ``` ### setlpFee ```solidity function setlpFee(PoolKey calldata key, uint16 value) external; ``` --- ## NativeERC20 [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/NativeERC20.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** Test *This token contract simulates the ERC20 representation of a native token where on `transfer` and `transferFrom` the native balances are modified using a precompile* ## State Variables ### name ```solidity string public name = "NativeERC20"; ``` ### symbol ```solidity string public symbol = "NERC20"; ``` ### decimals ```solidity uint8 public decimals = 18; ``` ### allowance ```solidity mapping(address => mapping(address => uint256)) public allowance; ``` ## Functions ### totalSupply ```solidity function totalSupply() public view returns (uint256); ``` ### approve ```solidity function approve(address guy, uint256 wad) public returns (bool); ``` ### transfer ```solidity function transfer(address dst, uint256 wad) public returns (bool); ``` ### transferFrom ```solidity function transferFrom(address src, address dst, uint256 wad) public returns (bool); ``` ### balanceOf ```solidity function balanceOf(address account) external view returns (uint256); ``` ## Events ### Approval ```solidity event Approval(address indexed src, address indexed guy, uint256 wad); ``` ### Transfer ```solidity event Transfer(address indexed src, address indexed dst, uint256 wad); ``` --- ## NoDelegateCallTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/NoDelegateCallTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [NoDelegateCall](contracts/v4/reference/core/NoDelegateCall.md) ## Functions ### canBeDelegateCalled ```solidity function canBeDelegateCalled() public view returns (uint256); ``` ### cannotBeDelegateCalled ```solidity function cannotBeDelegateCalled() public view noDelegateCall returns (uint256); ``` ### getGasCostOfCanBeDelegateCalled ```solidity function getGasCostOfCanBeDelegateCalled() external view returns (uint256); ``` ### getGasCostOfCannotBeDelegateCalled ```solidity function getGasCostOfCannotBeDelegateCalled() external view returns (uint256); ``` ### callsIntoNoDelegateCallFunction ```solidity function callsIntoNoDelegateCallFunction() external view; ``` ### noDelegateCallPrivate ```solidity function noDelegateCallPrivate() private view noDelegateCall; ``` --- ## PoolClaimsTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolClaimsTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [PoolTestBase](contracts/v4/reference/core/test/PoolTestBase.md) ## Functions ### constructor ```solidity constructor(IPoolManager _manager) PoolTestBase(_manager); ``` ### deposit Convert ERC20 into a claimable 6909 ```solidity function deposit(Currency currency, address user, uint256 amount) external payable; ``` ### withdraw Redeem claimable 6909 for ERC20 ```solidity function withdraw(Currency currency, address user, uint256 amount) external payable; ``` ### unlockCallback ```solidity function unlockCallback(bytes calldata rawData) external returns (bytes memory); ``` ## Structs ### CallbackData ```solidity struct CallbackData { address sender; address user; Currency currency; uint256 amount; bool deposit; } ``` --- ## PoolDonateTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolDonateTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [PoolTestBase](contracts/v4/reference/core/test/PoolTestBase.md) ## Functions ### constructor ```solidity constructor(IPoolManager _manager) PoolTestBase(_manager); ``` ### donate ```solidity function donate(PoolKey memory key, uint256 amount0, uint256 amount1, bytes memory hookData) external payable returns (BalanceDelta delta); ``` ### unlockCallback ```solidity function unlockCallback(bytes calldata rawData) external returns (bytes memory); ``` ## Structs ### CallbackData ```solidity struct CallbackData { address sender; PoolKey key; uint256 amount0; uint256 amount1; bytes hookData; } ``` --- ## PoolEmptyUnlockTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolEmptyUnlockTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IUnlockCallback](/src/interfaces/callback/IUnlockCallback.sol/interface.IUnlockCallback.md) ## State Variables ### manager ```solidity IPoolManager manager; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _manager); ``` ### unlock ```solidity function unlock() external; ``` ### unlockCallback Called by the pool manager on `msg.sender` when the manager is unlocked ```solidity function unlockCallback(bytes calldata) external override returns (bytes memory); ``` ## Events ### UnlockCallback ```solidity event UnlockCallback(); ``` --- ## PoolModifyLiquidityTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolModifyLiquidityTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [PoolTestBase](contracts/v4/reference/core/test/PoolTestBase.md) ## Functions ### constructor ```solidity constructor(IPoolManager _manager) PoolTestBase(_manager); ``` ### modifyLiquidity ```solidity function modifyLiquidity(PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, bytes memory hookData) external payable returns (BalanceDelta delta); ``` ### modifyLiquidity ```solidity function modifyLiquidity( PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, bytes memory hookData, bool settleUsingBurn, bool takeClaims ) public payable returns (BalanceDelta delta); ``` ### unlockCallback ```solidity function unlockCallback(bytes calldata rawData) external returns (bytes memory); ``` ## Structs ### CallbackData ```solidity struct CallbackData { address sender; PoolKey key; IPoolManager.ModifyLiquidityParams params; bytes hookData; bool settleUsingBurn; bool takeClaims; } ``` --- ## PoolModifyLiquidityTestNoChecks [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolModifyLiquidityTestNoChecks.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [PoolTestBase](contracts/v4/reference/core/test/PoolTestBase.md) ## Functions ### constructor ```solidity constructor(IPoolManager _manager) PoolTestBase(_manager); ``` ### modifyLiquidity ```solidity function modifyLiquidity(PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, bytes memory hookData) external payable returns (BalanceDelta delta); ``` ### modifyLiquidity ```solidity function modifyLiquidity( PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, bytes memory hookData, bool settleUsingBurn, bool takeClaims ) public payable returns (BalanceDelta delta); ``` ### unlockCallback ```solidity function unlockCallback(bytes calldata rawData) external returns (bytes memory); ``` ## Structs ### CallbackData ```solidity struct CallbackData { address sender; PoolKey key; IPoolManager.ModifyLiquidityParams params; bytes hookData; bool settleUsingBurn; bool takeClaims; } ``` --- ## Action [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolNestedActionsTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ```solidity enum Action { NESTED_SELF_UNLOCK, NESTED_EXECUTOR_UNLOCK, SWAP_AND_SETTLE, DONATE_AND_SETTLE, ADD_LIQUIDITY_AND_SETTLE, REMOVE_LIQUIDITY_AND_SETTLE, INITIALIZE } ``` # PoolNestedActionsTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolNestedActionsTest.sol) **Inherits:** Test, [IUnlockCallback](/src/interfaces/callback/IUnlockCallback.sol/interface.IUnlockCallback.md) ## State Variables ### manager ```solidity IPoolManager manager; ``` ### executor ```solidity NestedActionExecutor public executor; ``` ### user ```solidity address user; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _manager); ``` ### unlock ```solidity function unlock(bytes calldata data) external; ``` ### unlockCallback Called by the pool manager on `msg.sender` when the manager is unlocked ```solidity function unlockCallback(bytes calldata data) external override returns (bytes memory); ``` ### _nestedUnlock ```solidity function _nestedUnlock() internal; ``` # NestedActionExecutor [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolNestedActionsTest.sol) **Inherits:** Test, [PoolTestBase](contracts/v4/reference/core/test/PoolTestBase.md) ## State Variables ### key ```solidity PoolKey internal key; ``` ### user ```solidity address user; ``` ### ADD_LIQUIDITY_PARAMS ```solidity IPoolManager.ModifyLiquidityParams internal ADD_LIQUIDITY_PARAMS = IPoolManager.ModifyLiquidityParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18, salt: 0}); ``` ### REMOVE_LIQUIDITY_PARAMS ```solidity IPoolManager.ModifyLiquidityParams internal REMOVE_LIQUIDITY_PARAMS = IPoolManager.ModifyLiquidityParams({tickLower: -120, tickUpper: 120, liquidityDelta: -1e18, salt: 0}); ``` ### SWAP_PARAMS ```solidity IPoolManager.SwapParams internal SWAP_PARAMS = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -100, sqrtPriceLimitX96: Constants.SQRT_PRICE_1_2}); ``` ### DONATE_AMOUNT0 ```solidity uint256 internal DONATE_AMOUNT0 = 12345e6; ``` ### DONATE_AMOUNT1 ```solidity uint256 internal DONATE_AMOUNT1 = 98765e4; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _manager, address _user) PoolTestBase(_manager); ``` ### setKey ```solidity function setKey(PoolKey memory _key) external; ``` ### execute ```solidity function execute(Action[] memory actions) public; ``` ### _nestedUnlock ```solidity function _nestedUnlock() internal; ``` ### _swap ```solidity function _swap(address caller) internal; ``` ### _addLiquidity ```solidity function _addLiquidity(address caller) internal; ``` ### _removeLiquidity ```solidity function _removeLiquidity(address caller) internal; ``` ### _donate ```solidity function _donate(address caller) internal; ``` ### _initialize ```solidity function _initialize() internal; ``` ### unlockCallback ```solidity function unlockCallback(bytes calldata) external pure override returns (bytes memory); ``` ## Errors ### KeyNotSet ```solidity error KeyNotSet(); ``` --- ## PoolSwapTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolSwapTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [PoolTestBase](contracts/v4/reference/core/test/PoolTestBase.md) ## Functions ### constructor ```solidity constructor(IPoolManager _manager) PoolTestBase(_manager); ``` ### swap ```solidity function swap( PoolKey memory key, IPoolManager.SwapParams memory params, TestSettings memory testSettings, bytes memory hookData ) external payable returns (BalanceDelta delta); ``` ### unlockCallback ```solidity function unlockCallback(bytes calldata rawData) external returns (bytes memory); ``` ## Errors ### NoSwapOccurred ```solidity error NoSwapOccurred(); ``` ## Structs ### CallbackData ```solidity struct CallbackData { address sender; TestSettings testSettings; PoolKey key; IPoolManager.SwapParams params; bytes hookData; } ``` ### TestSettings ```solidity struct TestSettings { bool takeClaims; bool settleUsingBurn; } ``` --- ## PoolTakeTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolTakeTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [PoolTestBase](contracts/v4/reference/core/test/PoolTestBase.md) ## Functions ### constructor ```solidity constructor(IPoolManager _manager) PoolTestBase(_manager); ``` ### take ```solidity function take(PoolKey memory key, uint256 amount0, uint256 amount1) external payable; ``` ### unlockCallback ```solidity function unlockCallback(bytes calldata rawData) external returns (bytes memory); ``` ### _testTake ```solidity function _testTake(Currency currency, address sender, uint256 amount) internal; ``` ## Structs ### CallbackData ```solidity struct CallbackData { address sender; PoolKey key; uint256 amount0; uint256 amount1; } ``` --- ## PoolTestBase [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/PoolTestBase.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IUnlockCallback](/src/interfaces/callback/IUnlockCallback.sol/interface.IUnlockCallback.md) ## State Variables ### manager ```solidity IPoolManager public immutable manager; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _manager); ``` ### _fetchBalances ```solidity function _fetchBalances(Currency currency, address user, address deltaHolder) internal view returns (uint256 userBalance, uint256 poolBalance, int256 delta); ``` --- ## ProtocolFeesImplementation [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/ProtocolFeesImplementation.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [ProtocolFees](contracts/v4/reference/core/ProtocolFees.md) ## State Variables ### _pools ```solidity mapping(PoolId id => Pool.State) internal _pools; ``` ### isUnlocked ```solidity bool internal isUnlocked; ``` ## Functions ### constructor ```solidity constructor() ProtocolFees(msg.sender); ``` ### setPrice ```solidity function setPrice(PoolKey memory key, uint160 sqrtPriceX96) public; ``` ### _getPool ```solidity function _getPool(PoolId id) internal view override returns (Pool.State storage); ``` ### setIsUnlocked ```solidity function setIsUnlocked(bool newValue) public; ``` ### _isUnlocked ```solidity function _isUnlocked() internal view override returns (bool); ``` ### updateProtocolFees ```solidity function updateProtocolFees(Currency currency, uint256 amount) public; ``` --- ## ProxyPoolManager [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/ProxyPoolManager.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IPoolManager](contracts/v4/reference/core/interfaces/IPoolManager.md), [ProtocolFees](contracts/v4/reference/core/ProtocolFees.md), [NoDelegateCall](contracts/v4/reference/core/NoDelegateCall.md), [ERC6909Claims](contracts/v4/reference/core/ERC6909Claims.md), [Extsload](contracts/v4/reference/core/Extsload.md), [Exttload](contracts/v4/reference/core/Exttload.md) A proxy pool manager that delegates calls to the real/delegate pool manager ## State Variables ### MAX_TICK_SPACING ```solidity int24 private constant MAX_TICK_SPACING = TickMath.MAX_TICK_SPACING; ``` ### MIN_TICK_SPACING ```solidity int24 private constant MIN_TICK_SPACING = TickMath.MIN_TICK_SPACING; ``` ### _pools ```solidity mapping(PoolId id => Pool.State) internal _pools; ``` ### _delegateManager ```solidity address internal immutable _delegateManager; ``` ## Functions ### constructor ```solidity constructor(address delegateManager) ProtocolFees(msg.sender); ``` ### onlyWhenUnlocked This will revert if the contract is locked ```solidity modifier onlyWhenUnlocked(); ``` ### unlock All interactions on the contract that account deltas require unlocking. A caller that calls `unlock` must implement `IUnlockCallback(msg.sender).unlockCallback(data)`, where they interact with the remaining functions on this contract. *The only functions callable without an unlocking are `initialize` and `updateDynamicLPFee`* ```solidity function unlock(bytes calldata data) external noDelegateCall returns (bytes memory result); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`data`|`bytes`|Any data to pass to the callback, via `IUnlockCallback(msg.sender).unlockCallback(data)`| **Returns** |Name|Type|Description| |----|----|-----------| |`result`|`bytes`|The data returned by the call to `IUnlockCallback(msg.sender).unlockCallback(data)`| ### initialize Initialize the state for a given pool ID *A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee* ```solidity function initialize(PoolKey memory key, uint160 sqrtPriceX96) external noDelegateCall returns (int24 tick); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool key for the pool to initialize| |`sqrtPriceX96`|`uint160`|The initial square root price| **Returns** |Name|Type|Description| |----|----|-----------| |`tick`|`int24`|The initial tick of the pool| ### modifyLiquidity Modify the liquidity for the given pool *Poke by calling with a zero liquidityDelta* ```solidity function modifyLiquidity(PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, bytes calldata hookData) external onlyWhenUnlocked noDelegateCall returns (BalanceDelta callerDelta, BalanceDelta feesAccrued); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool to modify liquidity in| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for modifying the liquidity| |`hookData`|`bytes`|The data to pass through to the add/removeLiquidity hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`callerDelta`|`BalanceDelta`|The balance delta of the caller of modifyLiquidity. This is the total of both principal, fee deltas, and hook deltas if applicable| |`feesAccrued`|`BalanceDelta`|The balance delta of the fees generated in the liquidity range. Returned for informational purposes| ### swap Swap against the given pool *Swapping on low liquidity pools may cause unexpected swap amounts when liquidity available is less than amountSpecified. Additionally note that if interacting with hooks that have the BEFORE_SWAP_RETURNS_DELTA_FLAG or AFTER_SWAP_RETURNS_DELTA_FLAG the hook may alter the swap input/output. Integrators should perform checks on the returned swapDelta.* ```solidity function swap(PoolKey memory key, IPoolManager.SwapParams memory params, bytes calldata hookData) external onlyWhenUnlocked noDelegateCall returns (BalanceDelta swapDelta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The pool to swap in| |`params`|`IPoolManager.SwapParams`|The parameters for swapping| |`hookData`|`bytes`|The data to pass through to the swap hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`swapDelta`|`BalanceDelta`|The balance delta of the address swapping| ### donate Donate the given currency amounts to the in-range liquidity providers of a pool *Calls to donate can be frontrun adding just-in-time liquidity, with the aim of receiving a portion donated funds. Donors should keep this in mind when designing donation mechanisms.* ```solidity function donate(PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData) external onlyWhenUnlocked noDelegateCall returns (BalanceDelta delta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The key of the pool to donate to| |`amount0`|`uint256`|The amount of currency0 to donate| |`amount1`|`uint256`|The amount of currency1 to donate| |`hookData`|`bytes`|The data to pass through to the donate hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`delta`|`BalanceDelta`|BalanceDelta The delta of the caller after the donate| ### sync Writes the current ERC20 balance of the specified currency to transient storage This is used to checkpoint balances for the manager and derive deltas for the caller. *This MUST be called before any ERC20 tokens are sent into the contract, but can be skipped for native tokens because the amount to settle is determined by the sent value. However, if an ERC20 token has been synced and not settled, and the caller instead wants to settle native funds, this function can be called with the native currency to then be able to settle the native currency* ```solidity function sync(Currency currency) public; ``` ### take Called by the user to net out some value owed to the user *Will revert if the requested amount is not available, consider using `mint` instead* ```solidity function take(Currency currency, address to, uint256 amount) external onlyWhenUnlocked noDelegateCall; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`Currency`|The currency to withdraw from the pool manager| |`to`|`address`|The address to withdraw to| |`amount`|`uint256`|The amount of currency to withdraw| ### settle Called by the user to pay what is owed ```solidity function settle() external payable onlyWhenUnlocked noDelegateCall returns (uint256 paid); ``` **Returns** |Name|Type|Description| |----|----|-----------| |`paid`|`uint256`|The amount of currency settled| ### settleFor Called by the user to pay on behalf of another address ```solidity function settleFor(address recipient) external payable onlyWhenUnlocked noDelegateCall returns (uint256 paid); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`recipient`|`address`|The address to credit for the payment| **Returns** |Name|Type|Description| |----|----|-----------| |`paid`|`uint256`|The amount of currency settled| ### clear WARNING - Any currency that is cleared, will be non-retrievable, and locked in the contract permanently. A call to clear will zero out a positive balance WITHOUT a corresponding transfer. *This could be used to clear a balance that is considered dust. Additionally, the amount must be the exact positive balance. This is to enforce that the caller is aware of the amount being cleared.* ```solidity function clear(Currency currency, uint256 amount) external onlyWhenUnlocked; ``` ### mint Called by the user to move value into ERC6909 balance *The id is converted to a uint160 to correspond to a currency address If the upper 12 bytes are not 0, they will be 0-ed out* ```solidity function mint(address to, uint256 id, uint256 amount) external onlyWhenUnlocked noDelegateCall; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`to`|`address`|The address to mint the tokens to| |`id`|`uint256`|The currency address to mint to ERC6909s, as a uint256| |`amount`|`uint256`|The amount of currency to mint| ### burn Called by the user to move value from ERC6909 balance *The id is converted to a uint160 to correspond to a currency address If the upper 12 bytes are not 0, they will be 0-ed out* ```solidity function burn(address from, uint256 id, uint256 amount) external onlyWhenUnlocked noDelegateCall; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`from`|`address`|The address to burn the tokens from| |`id`|`uint256`|The currency address to burn from ERC6909s, as a uint256| |`amount`|`uint256`|The amount of currency to burn| ### updateDynamicLPFee Updates the pools lp fees for the a pool that has enabled dynamic lp fees. *A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee* ```solidity function updateDynamicLPFee(PoolKey memory key, uint24 newDynamicLPFee) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The key of the pool to update dynamic LP fees for| |`newDynamicLPFee`|`uint24`|The new dynamic pool LP fee| ### _delegateCall Make a delegate call, bubble up any error or return the result ```solidity function _delegateCall(address target, bytes memory data) internal returns (bytes memory result); ``` ### _getPool Implementation of the _getPool function defined in ProtocolFees ```solidity function _getPool(PoolId id) internal view override returns (Pool.State storage); ``` ### _isUnlocked Implementation of the _isUnlocked function defined in ProtocolFees ```solidity function _isUnlocked() internal view override returns (bool); ``` --- ## SkipCallsTestHook [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/SkipCallsTestHook.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [BaseTestHooks](contracts/v4/reference/core/test/BaseTestHooks.md), Test ## State Variables ### counter ```solidity uint256 public counter; ``` ### manager ```solidity IPoolManager manager; ``` ## Functions ### setManager ```solidity function setManager(IPoolManager _manager) external; ``` ### beforeInitialize ```solidity function beforeInitialize(address, PoolKey calldata key, uint160 sqrtPriceX96) external override returns (bytes4); ``` ### afterInitialize ```solidity function afterInitialize(address, PoolKey calldata key, uint160 sqrtPriceX96, int24) external override returns (bytes4); ``` ### beforeAddLiquidity ```solidity function beforeAddLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, bytes calldata hookData ) external override returns (bytes4); ``` ### afterAddLiquidity ```solidity function afterAddLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, BalanceDelta, BalanceDelta, bytes calldata hookData ) external override returns (bytes4, BalanceDelta); ``` ### beforeRemoveLiquidity ```solidity function beforeRemoveLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, bytes calldata hookData ) external override returns (bytes4); ``` ### afterRemoveLiquidity ```solidity function afterRemoveLiquidity( address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, BalanceDelta, BalanceDelta, bytes calldata hookData ) external override returns (bytes4, BalanceDelta); ``` ### beforeSwap ```solidity function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata hookData) external override returns (bytes4, BeforeSwapDelta, uint24); ``` ### afterSwap ```solidity function afterSwap( address, PoolKey calldata key, IPoolManager.SwapParams calldata params, BalanceDelta, bytes calldata hookData ) external override returns (bytes4, int128); ``` ### beforeDonate ```solidity function beforeDonate(address, PoolKey calldata key, uint256 amt0, uint256 amt1, bytes calldata hookData) external override returns (bytes4); ``` ### afterDonate ```solidity function afterDonate(address, PoolKey calldata key, uint256 amt0, uint256 amt1, bytes calldata hookData) external override returns (bytes4); ``` ### _initialize ```solidity function _initialize(PoolKey memory key, uint160 sqrtPriceX96) public; ``` ### _swap ```solidity function _swap(PoolKey calldata key, IPoolManager.SwapParams memory params, bytes calldata hookData) public; ``` ### _addLiquidity ```solidity function _addLiquidity(PoolKey calldata key, IPoolManager.ModifyLiquidityParams memory params, bytes calldata hookData) public; ``` ### _removeLiquidity ```solidity function _removeLiquidity( PoolKey calldata key, IPoolManager.ModifyLiquidityParams memory params, bytes calldata hookData ) public; ``` ### _donate ```solidity function _donate(PoolKey calldata key, uint256 amt0, uint256 amt1, bytes calldata hookData) public; ``` --- ## SqrtPriceMathEchidnaTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/SqrtPriceMathEchidnaTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## Functions ### mulDivRoundingUpInvariants ```solidity function mulDivRoundingUpInvariants(uint256 x, uint256 y, uint256 z) external pure; ``` ### getNextSqrtPriceFromInputInvariants ```solidity function getNextSqrtPriceFromInputInvariants(uint160 sqrtP, uint128 liquidity, uint256 amountIn, bool zeroForOne) external pure; ``` ### getNextSqrtPriceFromOutputInvariants ```solidity function getNextSqrtPriceFromOutputInvariants(uint160 sqrtP, uint128 liquidity, uint256 amountOut, bool zeroForOne) external pure; ``` ### getNextSqrtPriceFromAmount0RoundingUpInvariants ```solidity function getNextSqrtPriceFromAmount0RoundingUpInvariants(uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add) external pure; ``` ### getNextSqrtPriceFromAmount1RoundingDownInvariants ```solidity function getNextSqrtPriceFromAmount1RoundingDownInvariants( uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add ) external pure; ``` ### getAmount0DeltaInvariants ```solidity function getAmount0DeltaInvariants(uint160 sqrtP, uint160 sqrtQ, uint128 liquidity) external pure; ``` ### getAmount0DeltaEquivalency ```solidity function getAmount0DeltaEquivalency(uint160 sqrtP, uint160 sqrtQ, uint128 liquidity, bool roundUp) external pure; ``` ### getAmount1DeltaInvariants ```solidity function getAmount1DeltaInvariants(uint160 sqrtP, uint160 sqrtQ, uint128 liquidity) external pure; ``` ### getAmount0DeltaSignedInvariants ```solidity function getAmount0DeltaSignedInvariants(uint160 sqrtP, uint160 sqrtQ, int128 liquidity) external pure; ``` ### getAmount1DeltaSignedInvariants ```solidity function getAmount1DeltaSignedInvariants(uint160 sqrtP, uint160 sqrtQ, int128 liquidity) external pure; ``` ### getOutOfRangeMintInvariants ```solidity function getOutOfRangeMintInvariants(uint160 sqrtA, uint160 sqrtB, int128 liquidity) external pure; ``` ### getInRangeMintInvariants ```solidity function getInRangeMintInvariants(uint160 sqrtLower, uint160 sqrtCurrent, uint160 sqrtUpper, int128 liquidity) external pure; ``` --- ## SwapRouterNoChecks [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/SwapRouterNoChecks.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [PoolTestBase](contracts/v4/reference/core/test/PoolTestBase.md) ## Functions ### constructor ```solidity constructor(IPoolManager _manager) PoolTestBase(_manager); ``` ### swap ```solidity function swap(PoolKey memory key, IPoolManager.SwapParams memory params) external payable; ``` ### unlockCallback ```solidity function unlockCallback(bytes calldata rawData) external returns (bytes memory); ``` ## Errors ### NoSwapOccurred ```solidity error NoSwapOccurred(); ``` ## Structs ### CallbackData ```solidity struct CallbackData { address sender; PoolKey key; IPoolManager.SwapParams params; } ``` --- ## TestERC20 [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/TestERC20.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IERC20Minimal](contracts/v4/reference/core/interfaces/IERC20Minimal.md) ## State Variables ### balanceOf ```solidity mapping(address => uint256) public override balanceOf; ``` ### allowance ```solidity mapping(address => mapping(address => uint256)) public override allowance; ``` ## Functions ### constructor ```solidity constructor(uint256 amountToMint); ``` ### mint ```solidity function mint(address to, uint256 amount) public; ``` ### transfer ```solidity function transfer(address recipient, uint256 amount) external override returns (bool); ``` ### approve ```solidity function approve(address spender, uint256 amount) external override returns (bool); ``` ### transferFrom ```solidity function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool); ``` --- ## TestInvalidERC20 [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/TestInvalidERC20.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IERC20Minimal](contracts/v4/reference/core/interfaces/IERC20Minimal.md) ## State Variables ### balanceOf ```solidity mapping(address => uint256) public override balanceOf; ``` ### allowance ```solidity mapping(address => mapping(address => uint256)) public override allowance; ``` ## Functions ### constructor ```solidity constructor(uint256 amountToMint); ``` ### mint ```solidity function mint(address to, uint256 amount) public; ``` ### transfer ```solidity function transfer(address recipient, uint256 amount) external override returns (bool); ``` ### approve ```solidity function approve(address spender, uint256 amount) external override returns (bool); ``` ### transferFrom ```solidity function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool); ``` --- ## TickMathEchidnaTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/TickMathEchidnaTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## Functions ### checkGetSqrtPriceAtTickInvariants ```solidity function checkGetSqrtPriceAtTickInvariants(int24 tick) external pure; ``` ### checkGetTickAtSqrtPriceInvariants ```solidity function checkGetTickAtSqrtPriceInvariants(uint160 price) external pure; ``` --- ## TickMathTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/TickMathTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## Functions ### getSqrtPriceAtTick ```solidity function getSqrtPriceAtTick(int24 tick) external pure returns (uint160); ``` ### getGasCostOfGetSqrtPriceAtTick ```solidity function getGasCostOfGetSqrtPriceAtTick(int24 tick) external view returns (uint256); ``` ### getTickAtSqrtPrice ```solidity function getTickAtSqrtPrice(uint160 sqrtPriceX96) external pure returns (int24); ``` ### getGasCostOfGetTickAtSqrtPrice ```solidity function getGasCostOfGetTickAtSqrtPrice(uint160 sqrtPriceX96) external view returns (uint256); ``` ### MIN_SQRT_PRICE ```solidity function MIN_SQRT_PRICE() external pure returns (uint160); ``` ### MAX_SQRT_PRICE ```solidity function MAX_SQRT_PRICE() external pure returns (uint160); ``` ### MIN_TICK ```solidity function MIN_TICK() external pure returns (int24); ``` ### MAX_TICK ```solidity function MAX_TICK() external pure returns (int24); ``` --- ## TickOverflowSafetyEchidnaTest [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/test/TickOverflowSafetyEchidnaTest.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## State Variables ### MIN_TICK ```solidity int24 private constant MIN_TICK = -16; ``` ### MAX_TICK ```solidity int24 private constant MAX_TICK = 16; ``` ### pool ```solidity Pool.State private pool; ``` ### tick ```solidity int24 private tick = 0; ``` ### feeGrowthGlobal0X128 ```solidity uint256 feeGrowthGlobal0X128 = type(uint256).max / 2; ``` ### feeGrowthGlobal1X128 ```solidity uint256 feeGrowthGlobal1X128 = type(uint256).max / 2; ``` ### totalLiquidity ```solidity int256 totalLiquidity = 0; ``` ### totalGrowth0 ```solidity uint256 private totalGrowth0 = 0; ``` ### totalGrowth1 ```solidity uint256 private totalGrowth1 = 0; ``` ## Functions ### increaseFeeGrowthGlobal0X128 ```solidity function increaseFeeGrowthGlobal0X128(uint256 amount) external; ``` ### increaseFeeGrowthGlobal1X128 ```solidity function increaseFeeGrowthGlobal1X128(uint256 amount) external; ``` ### setPosition ```solidity function setPosition(int24 tickLower, int24 tickUpper, int128 liquidityDelta) external; ``` ### moveToTick ```solidity function moveToTick(int24 target) external; ``` --- ## BalanceDelta [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BalanceDelta.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) *Two `int128` values packed into a single `int256` where the upper 128 bits represent the amount0 and the lower 128 bits represent the amount1.* ```solidity type BalanceDelta is int256; ``` ## BalanceDeltaLibrary [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BalanceDelta.sol) Library for getting the amount0 and amount1 deltas from the BalanceDelta type ## State Variables ### ZERO_DELTA A BalanceDelta of 0 ```solidity BalanceDelta public constant ZERO_DELTA = BalanceDelta.wrap(0); ``` ## Functions ### amount0 ```solidity function amount0(BalanceDelta balanceDelta) internal pure returns (int128 _amount0); ``` ### amount1 ```solidity function amount1(BalanceDelta balanceDelta) internal pure returns (int128 _amount1); ``` # sub [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BalanceDelta.sol) ```solidity function sub(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta); ``` # toBalanceDelta [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BalanceDelta.sol) ```solidity function toBalanceDelta(int128 _amount0, int128 _amount1) pure returns (BalanceDelta balanceDelta); ``` # eq [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BalanceDelta.sol) ```solidity function eq(BalanceDelta a, BalanceDelta b) pure returns (bool); ``` # add [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BalanceDelta.sol) ```solidity function add(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta); ``` # neq [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BalanceDelta.sol) ```solidity function neq(BalanceDelta a, BalanceDelta b) pure returns (bool); ``` --- ## BeforeSwapDelta [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BeforeSwapDelta.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ```solidity type BeforeSwapDelta is int256; ``` ## BeforeSwapDeltaLibrary [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BeforeSwapDelta.sol) Library for getting the specified and unspecified deltas from the BeforeSwapDelta type ## State Variables ### ZERO_DELTA A BeforeSwapDelta of 0 ```solidity BeforeSwapDelta public constant ZERO_DELTA = BeforeSwapDelta.wrap(0); ``` ## Functions ### getSpecifiedDelta extracts int128 from the upper 128 bits of the BeforeSwapDelta returned by beforeSwap ```solidity function getSpecifiedDelta(BeforeSwapDelta delta) internal pure returns (int128 deltaSpecified); ``` ### getUnspecifiedDelta extracts int128 from the lower 128 bits of the BeforeSwapDelta returned by beforeSwap and afterSwap ```solidity function getUnspecifiedDelta(BeforeSwapDelta delta) internal pure returns (int128 deltaUnspecified); ``` # toBeforeSwapDelta [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/BeforeSwapDelta.sol) ```solidity function toBeforeSwapDelta(int128 deltaSpecified, int128 deltaUnspecified) pure returns (BeforeSwapDelta beforeSwapDelta); ``` --- ## Currency [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/Currency.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ```solidity type Currency is address; ``` ## CurrencyLibrary [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/Currency.sol) *This library allows for transferring and holding native tokens and ERC20 tokens* ## State Variables ### ADDRESS_ZERO A constant to represent the native currency ```solidity Currency public constant ADDRESS_ZERO = Currency.wrap(address(0)); ``` ## Functions ### transfer ```solidity function transfer(Currency currency, address to, uint256 amount) internal; ``` ### balanceOfSelf ```solidity function balanceOfSelf(Currency currency) internal view returns (uint256); ``` ### balanceOf ```solidity function balanceOf(Currency currency, address owner) internal view returns (uint256); ``` ### isAddressZero ```solidity function isAddressZero(Currency currency) internal pure returns (bool); ``` ### toId ```solidity function toId(Currency currency) internal pure returns (uint256); ``` ### fromId ```solidity function fromId(uint256 id) internal pure returns (Currency); ``` ## Errors ### NativeTransferFailed Additional context for ERC-7751 wrapped error when a native transfer fails ```solidity error NativeTransferFailed(); ``` ### ERC20TransferFailed Additional context for ERC-7751 wrapped error when an ERC20 transfer fails ```solidity error ERC20TransferFailed(); ``` # greaterThanOrEqualTo [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/Currency.sol) ```solidity function greaterThanOrEqualTo(Currency currency, Currency other) pure returns (bool); ``` # lessThan [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/Currency.sol) ```solidity function lessThan(Currency currency, Currency other) pure returns (bool); ``` # equals [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/Currency.sol) ```solidity function equals(Currency currency, Currency other) pure returns (bool); ``` # greaterThan [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/Currency.sol) ```solidity function greaterThan(Currency currency, Currency other) pure returns (bool); ``` --- ## PoolId [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/PoolId.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ```solidity type PoolId is bytes32; ``` ## PoolIdLibrary [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/PoolId.sol) Library for computing the ID of a pool ## Functions ### toId Returns value equal to keccak256(abi.encode(poolKey)) ```solidity function toId(PoolKey memory poolKey) internal pure returns (PoolId poolId); ``` --- ## PoolKey [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/PoolKey.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Returns the key for identifying a pool ```solidity struct PoolKey { Currency currency0; Currency currency1; uint24 fee; int24 tickSpacing; IHooks hooks; } ``` --- ## Slot0 [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/Slot0.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) *Slot0 is a packed version of solidity structure. Using the packaged version saves gas by not storing the structure fields in memory slots. Layout: 24 bits empty | 24 bits lpFee | 12 bits protocolFee 1->0 | 12 bits protocolFee 0->1 | 24 bits tick | 160 bits sqrtPriceX96 Fields in the direction from the least significant bit: The current price uint160 sqrtPriceX96; The current tick int24 tick; Protocol fee, expressed in hundredths of a bip, upper 12 bits are for 1->0, and the lower 12 are for 0->1 the maximum is 1000 - meaning the maximum protocol fee is 0.1% the protocolFee is taken from the input first, then the lpFee is taken from the remaining input uint24 protocolFee; The current LP fee of the pool. If the pool is dynamic, this does not include the dynamic fee flag. uint24 lpFee;* ```solidity type Slot0 is bytes32; ``` # Slot0Library [Git Source](https://github.com/uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/types/Slot0.sol) Library for getting and setting values in the Slot0 type ## State Variables ### MASK_160_BITS ```solidity uint160 internal constant MASK_160_BITS = 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; ``` ### MASK_24_BITS ```solidity uint24 internal constant MASK_24_BITS = 0xFFFFFF; ``` ### TICK_OFFSET ```solidity uint8 internal constant TICK_OFFSET = 160; ``` ### PROTOCOL_FEE_OFFSET ```solidity uint8 internal constant PROTOCOL_FEE_OFFSET = 184; ``` ### LP_FEE_OFFSET ```solidity uint8 internal constant LP_FEE_OFFSET = 208; ``` ## Functions ### sqrtPriceX96 ```solidity function sqrtPriceX96(Slot0 _packed) internal pure returns (uint160 _sqrtPriceX96); ``` ### tick ```solidity function tick(Slot0 _packed) internal pure returns (int24 _tick); ``` ### protocolFee ```solidity function protocolFee(Slot0 _packed) internal pure returns (uint24 _protocolFee); ``` ### lpFee ```solidity function lpFee(Slot0 _packed) internal pure returns (uint24 _lpFee); ``` ### setSqrtPriceX96 ```solidity function setSqrtPriceX96(Slot0 _packed, uint160 _sqrtPriceX96) internal pure returns (Slot0 _result); ``` ### setTick ```solidity function setTick(Slot0 _packed, int24 _tick) internal pure returns (Slot0 _result); ``` ### setProtocolFee ```solidity function setProtocolFee(Slot0 _packed, uint24 _protocolFee) internal pure returns (Slot0 _result); ``` ### setLpFee ```solidity function setLpFee(Slot0 _packed, uint24 _lpFee) internal pure returns (Slot0 _result); ``` --- ## BalanceDelta Guide `BalanceDelta` is a type used in Uniswap V4 to represent the balance changes of two tokens (token0 and token1). It tightly packs the two values in a single 256 bits. It is designed to efficiently store and manipulate these balance deltas, with the upper 128 bits representing the change in token0 (`amount0`) and the lower 128 bits representing the change in token1 (`amount1`). ## Purpose The main purpose of `BalanceDelta` is to keep track of the net balance changes in the two tokens of a pool after various operations such as swaps, liquidity modifications, and interactions with hooks. It provides a compact and efficient way to store and update these balance deltas throughout the execution flow of the pool. In the context of hooks, `BalanceDelta` is used to ensure that the net balance change for each token is zero after the hook's functionality is executed. This is important for maintaining the integrity of the pool's balances and ensuring that the hooks do not introduce any unexpected or unauthorized balance changes. ## Type Definition ```solidity type BalanceDelta is int256; ``` ## Using Directives ```solidity using {add as +, sub as -, eq as ==, neq as !=} for BalanceDelta global; using BalanceDeltaLibrary for BalanceDelta global; using SafeCast for int256; ``` These using directives enable arithmetic operations, equality comparisons, and library functions to be used directly on `BalanceDelta` values. ## Functions ### toBalanceDelta ```solidity function toBalanceDelta(int128 amount0, int128 amount1) pure returns (BalanceDelta balanceDelta); ``` Creates a `BalanceDelta` value from two `int128` values representing `amount0` and `amount1`. | Param Name | Type | Description | |------------|---------|--------------------------------------| | amount0 | int128 | The amount for the first token | | amount1 | int128 | The amount for the second token | Returns the created `BalanceDelta` value. ### add ```solidity function add(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta); ``` Adds two `BalanceDelta` values. | Param Name | Type | Description | |------------|--------------|--------------------------------------| | a | BalanceDelta | The first `BalanceDelta` value | | b | BalanceDelta | The second `BalanceDelta` value | Returns the sum of the two `BalanceDelta` values. ### sub ```solidity function sub(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta); ``` Subtracts one `BalanceDelta` value from another. | Param Name | Type | Description | |------------|--------------|--------------------------------------| | a | BalanceDelta | The first `BalanceDelta` value | | b | BalanceDelta | The second `BalanceDelta` value | Returns the difference of the two `BalanceDelta` values. ### eq ```solidity function eq(BalanceDelta a, BalanceDelta b) pure returns (bool); ``` Checks if two `BalanceDelta` values are equal. | Param Name | Type | Description | |------------|--------------|--------------------------------------| | a | BalanceDelta | The first `BalanceDelta` value | | b | BalanceDelta | The second `BalanceDelta` value | Returns `true` if the values are equal, `false` otherwise. ### neq ```solidity function neq(BalanceDelta a, BalanceDelta b) pure returns (bool); ``` Checks if two `BalanceDelta` values are not equal. | Param Name | Type | Description | |------------|--------------|--------------------------------------| | a | BalanceDelta | The first `BalanceDelta` value | | b | BalanceDelta | The second `BalanceDelta` value | Returns `true` if the values are not equal, `false` otherwise. ## Library Functions ### amount0 ```solidity function amount0(BalanceDelta balanceDelta) internal pure returns (int128 _amount0); ``` Extracts the `amount0` value from a `BalanceDelta`. | Param Name | Type | Description | |--------------|--------------|--------------------------------------| | balanceDelta | BalanceDelta | The `BalanceDelta` value | Returns the extracted `amount0` value as an `int128`. ### amount1 ```solidity function amount1(BalanceDelta balanceDelta) internal pure returns (int128 _amount1); ``` Extracts the `amount1` value from a `BalanceDelta`. | Param Name | Type | Description | |--------------|--------------|--------------------------------------| | balanceDelta | BalanceDelta | The `BalanceDelta` value | Returns the extracted `amount1` value as an `int128`. ## Usage in Hooks When a hook is called during a swap or liquidity modification, it can perform custom logic and interact with the pool's balances. However, to maintain the correctness of the pool's state, the hook must ensure that any balance changes it introduces are properly accounted for and net to zero at the end of its execution. The BalanceDelta is forwarded to the `afterSwap` & `afterAddliquidity`, `afterRemoveLiquidity` hooks. ## Usage in the Pool Library In the `Pool` library, `BalanceDelta` is used extensively to track balance changes during various operations such as swaps, liquidity modifications, and donations. The library functions `swap`, `modifyLiquidity`, and `donate` all return `BalanceDelta` values representing the net balance changes resulting from these operations. The `Pool` library uses `BalanceDelta` to efficiently update and manage the pool's balances, ensuring that the net balance changes are accurately accounted for and that the pool remains in a consistent state. By leveraging the compact representation and efficient arithmetic operations provided by `BalanceDelta`, the `Pool` library can perform complex balance calculations and updates in a gas-optimized manner, reducing the overall cost of executing pool-related operations. --- ## BeforeSwapDelta Guide `BeforeSwapDelta` is a custom type used in Uniswap V4 hook contracts to represent balance changes during swap operations. It is specifically designed to handle the return value of the `beforeSwap` hook and to be compatible with the `afterSwap` hook. Before explaining `BeforeSwapDelta` in detail, it is worth noting that in the context of Uniswap V4 swaps: - The **specified** token is the one for which the user specifies an exact input or output amount. - The **unspecified** token is the counterpart in the swap, whose amount is determined by the pool's pricing mechanism. ## Purpose The main purpose of `BeforeSwapDelta` is to efficiently encode and decode balance changes for both specified and unspecified tokens in a single 256-bit value. This compact representation allows for gas-efficient operations and seamless integration with Uniswap V4's hook system. `BeforeSwapDelta` is essential for: - Allowing hooks to modify swap parameters or override default swap behavior - Allowing hooks to take fees from swaps - Providing fine-grained control over balance adjustments resulting from swaps - Optimizing gas usage by packing two `int128` values into a single `int256` To summarise, `BeforeSwapDelta` is used to ensure that the net balance change for each token is zero after the hook's functionality is executed. This is important for maintaining the integrity of the pool's balances and ensuring that the hooks do not introduce any unexpected or unauthorized balance changes. ## Type Definition ```solidity type BeforeSwapDelta is int256; ``` The `BeforeSwapDelta` type is an alias for int256, where: - The upper 128 bits represent the delta in specified tokens - The lower 128 bits represent the delta in unspecified tokens ## Using Directives ```solidity using BeforeSwapDeltaLibrary for BeforeSwapDelta global; using SafeCast for int256; ``` These using directives enable library functions to be used directly on `BeforeSwapDelta` values and provide safe casting operations for int256 values. ## Functions ### toBeforeSwapDelta ```solidity function toBeforeSwapDelta(int128 deltaSpecified, int128 deltaUnspecified) pure returns (BeforeSwapDelta beforeSwapDelta); ``` Creates a `BeforeSwapDelta` value from two `int128` values representing `deltaSpecified` and `deltaUnspecified`. | Param Name | Type | Description | |------------|---------|--------------------------------------| | deltaSpecified | int128 | The balance change for the specified token | | deltaUnspecified | int128 | The balance change for the unspecified token | Returns the created `BeforeSwapDelta` value. This function uses bitwise operations in assembly for gas-efficient packing of the two int128 values: ```solidity assembly ("memory-safe") { beforeSwapDelta := or(shl(128, deltaSpecified), and(sub(shl(128, 1), 1), deltaUnspecified)) } ``` ## Library Functions ### ZERO_DELTA ```solidity BeforeSwapDelta public constant ZERO_DELTA = BeforeSwapDelta.wrap(0); ``` A constant representing a zero delta (no balance changes). It should be used as a default return value. It is most commonly used for hooks that are *not* implementing custom accounting. ### getSpecifiedDelta ```solidity function getSpecifiedDelta(BeforeSwapDelta delta) internal pure returns (int128 deltaSpecified); ``` Extracts the specified token delta from a `BeforeSwapDelta` value. | Param Name | Type | Description | |------------|--------------|--------------------------------------| | delta | BeforeSwapDelta | The `BeforeSwapDelta` value | Returns the extracted specified token delta as an `int128`. ### getUnspecifiedDelta ```solidity function getUnspecifiedDelta(BeforeSwapDelta delta) internal pure returns (int128 deltaUnspecified); ``` Extracts the unspecified token delta from a BeforeSwapDelta value. | Param Name | Type | Description | |------------|--------------|--------------------------------------| | delta | BeforeSwapDelta | The `BeforeSwapDelta` | Returns the extracted unspecified token delta as an `int128`. ## Usage in Hooks When a hook is called during a swap operation, it can perform custom logic and interact with the pool's balances. The `beforeSwap` hook returns a `BeforeSwapDelta` value to indicate any balance changes the *hook* introduces. For example, hooks taking fees should return the value it took as a `BeforeSwapDelta`. ## Usage in PoolManager.sol `BeforeSwapDelta` plays a crucial role in Uniswap V4's PoolManager contract, particularly in the swap process. Here's an overview of how it's used: ## Calling the `beforeSwap` Hook In the `swap` function of the PoolManager contract, the `beforeSwap` hook is called: ```solidity function swap(PoolKey memory key, IPoolManager.SwapParams memory params, bytes calldata hookData) // ... returns (BalanceDelta swapDelta) { // ... (other code) BeforeSwapDelta beforeSwapDelta; { int256 amountToSwap; uint24 lpFeeOverride; (amountToSwap, beforeSwapDelta, lpFeeOverride) = key.hooks.beforeSwap(key, params, hookData); // ... (swap execution) } // ... (other code) } ``` The `beforeSwap` hook returns a `BeforeSwapDelta` value along with other parameters. ### Interaction between `beforeSwapDelta` and `amountToSwap` The `beforeSwapDelta` returned by the hook is used in conjunction with `params.amountSpecified` to determine the final `amountToSwap`. This allows hooks to modify the swap amount based on their custom logic. Here's a more detailed explanation of how this works: 1. The `beforeSwap` hook returns a `BeforeSwapDelta` value. 2. The `getSpecifiedDelta()` of this `BeforeSwapDelta` is used to adjust the original `params.amountSpecified`. 3. This adjustment results in the final `amountToSwap` that will be used for the actual swap operation. Here's a simplified representation of this calculation: ```solidity int256 amountToSwap = params.amountSpecified + beforeSwapDelta.getSpecifiedDelta(); ``` In this example, the `amountToSwap` is calculated by adding the specified delta from `beforeSwapDelta` to the original `amountSpecified`. This calculation allows hooks to increase or decrease the swap amount, effectively implementing features like fees, rebates, or other custom logic. **Detailed Example:** Let's say a user wants to swap 100 tokens, but a hook implements a 1% fee: 1. `params.amountSpecified` would be 100 2. The hook calculates the fee as 1 token and returns a `beforeSwapDelta` with a specified delta of -1 3. `amountToSwap` is then calculated as 100 + (-1) = 99 This way, the actual amount swapped (99) reflects the fee taken by the hook, while still allowing the pool to execute the swap based on the original 100 token input from the user. Here's how the `beforeSwap` hook might handle this: ```solidity function _beforeSwap( address, PoolKey calldata, IPoolManager.SwapParams calldata params, bytes calldata ) internal override returns (int256 amountIn, BeforeSwapDelta delta, uint24) { int128 specifiedAmount = params.amountSpecified.toInt128(); int128 fee = specifiedAmount / 100; // 1% fee int128 adjustedAmount = specifiedAmount - fee; delta = BeforeSwapDelta.from(-fee, 0); // Fee taken from specified token amountIn = params.amountSpecified; // Original amount return (amountIn, delta, 0); } ``` After this hook executes: - `amountIn` remains 100 (the original `params.amountSpecified`) - `delta` represents a change of -1 in the specified token (the fee) Then, in the PoolManager: ```solidity int256 amountToSwap = params.amountSpecified + beforeSwapDelta.getSpecifiedDelta(); // This effectively calculates: 100 + (-1) = 99 ``` As a result: - The pool sees the full input amount of 100 tokens. - The actual amount swapped is 99 tokens. - The 1 token difference becomes the hook's fee. This mechanism allows hooks to influence the swap amount while maintaining transparency about the full input amount, enabling complex custom logic within the Uniswap V4 framework. ## Relation to `afterSwap` While `beforeSwapDelta` is primarily used in the `beforeSwap` hook, it also plays a role in the `afterSwap` process. Specifically: - The `afterSwap` hook receives the `beforeSwapDelta` as a parameter. - The unspecified delta (`beforeSwapDelta.getUnspecifiedDelta()`) is particularly important in the `afterSwap` context. - This unspecified delta is accounted for in `afterSwap`'s calculations, allowing for consistent balance tracking across the entire swap process. This mechanism ensures that: 1. Changes made by the `beforeSwap` hook are properly considered when finalizing the swap. 2. The `afterSwap` hook can make informed decisions based on the full context of the swap, including any modifications made in `beforeSwap`. 3. Complex swap logic can be implemented across multiple hook points while maintaining consistency. For example, if a fee was taken on the specified token in `beforeSwap`, the `afterSwap` hook can use this information to ensure the overall balance changes are correct, potentially adjusting the unspecified token amount accordingly. Developers implementing custom hooks should be aware of this relationship and ensure their `beforeSwap` and `afterSwap` implementations work together coherently, especially when implementing features like fees or rebates that affect token balances. ## Key Purposes of BeforeSwapDelta The `BeforeSwapDelta` serves several important purposes in the Uniswap V4 swap process: 1. **Customization of Swap Behavior:** It allows hooks to modify the swap parameters or even completely override the default swap behavior. 2. **Balance Adjustment:** The delta values can be used to adjust the final balance changes resulting from the swap, giving hooks fine-grained control over the swap's outcome. 3. **Gas Optimization:** By packing two `int128` values into a single `int256`, it reduces the number of stack variables and can lead to gas savings. 4. **Cross-Hook Communication:** It provides a way for the `beforeSwap` hook to pass information to the `afterSwap` hook, enabling more complex and stateful hook logic. 5. **Hook Fees Implementation:** `BeforeSwapDelta` offers flexible options for implementing hook fees: - Fees can be charged on either the specified or unspecified token. - Fees can be implemented in either the `beforeSwap` or `afterSwap` hook. - For `beforeSwap`: 1. Adjust the specified amount to account for the fee. 2. Useful for scenarios where the fee needs to be known before the swap execution. - For `afterSwap`: 1. Generally considered best practice to charge fees on the unspecified token. 2. Allows for more accurate fee calculation based on the actual swap outcome. - As a result, developers can implement various fee structures, such as: 1. Fixed fee amounts 2. Percentage-based fees 3. Tiered fee structures based on swap volume or other criteria Example of a simple percentage-based fee in `beforeSwap`: ```solidity int128 fee = specifiedAmount * FEE_PERCENTAGE / 100; int128 adjustedAmount = specifiedAmount - fee; delta = BeforeSwapDelta.from(-adjustedAmount, 0); ``` This flexibility in fee implementation allows developers to create sophisticated economic models within their Uniswap V4 hooks, tailoring the behavior to specific use cases while maintaining the efficiency and standardization provided by the `BeforeSwapDelta` structure. ## Perspective It's important to note that the `BeforeSwapDelta` is from the perspective of the hook itself, not the user. For example, if a user swaps 1 USDC for 1 USDT: - User gives 1 USDC: balance0OfUser decreases - Hook gets 1 USDC: balance0OfHook increases This perspective is key to correctly interpreting and manipulating the delta values within hook implementations. ## Implementation Details The `BeforeSwapDelta` type and its associated functions use low-level assembly code for efficient bit manipulation and gas optimization: - The `toBeforeSwapDelta` function uses bitwise operations (`shl`, `or`, `and`, `sub`) to pack two `int128` values into a single `int256`. - The `getSpecifiedDelta` function uses the `sar` (shift arithmetic right) operation to extract the upper 128 bits. - The `getUnspecifiedDelta` function uses the `signextend` operation to extract and sign-extend the lower 128 bits. The `toBeforeSwapDelta` function combines the specified and unspecified deltas into a single `int256` value using bitwise operations. The `getSpecifiedDelta` and `getUnspecifiedDelta` functions extract the respective deltas using bit shifting and sign extension. By leveraging this compact representation and efficient arithmetic operations, Uniswap V4 can perform complex balance calculations and updates in a gas-optimized manner, reducing the overall cost of executing pool-related operations. ## Implementation Considerations When working with `BeforeSwapDelta`, especially for implementing hook fees, consider the following: - **Fee Timing:** While fees can be implemented in either `beforeSwap` or `afterSwap`, charging fees on the unspecified token in `afterSwap` is often considered best practice. This approach can provide more accurate fee calculations based on the final swap amounts. - **Fee Direction:** Remember that the deltas in `BeforeSwapDelta` are from the perspective of the hook. A positive delta means the hook is receiving tokens, while a negative delta means the hook is paying out tokens. - **Consistency:** Ensure that your fee implementation is consistent across both `beforeSwap` and `afterSwap` hooks to maintain the integrity of the swap process. - **Gas Efficiency:** When implementing fees, consider the gas costs of your calculations. The compact nature of `BeforeSwapDelta` can help in optimizing gas usage, but complex fee structures might increase gas costs. ## Comparison with BalanceDelta `BeforeSwapDelta` shares a similar structure with `BalanceDelta`, both packing two `int128` values into a single `int256`. However, there are key differences: - `BalanceDelta` represents amount0 and amount1. - `BeforeSwapDelta` represents specified and unspecified amounts, which may not directly correspond to token0 and token1, depending on the swap direction. ## Best Practices When working with `BeforeSwapDelta`, consider the following best practices: - Always use the provided library functions (`getSpecifiedDelta` and `getUnspecifiedDelta`) to extract delta values. - Ensure that the signs of the delta values are correct from the hook's perspective. - Use `SafeCast` when converting between different integer types to prevent overflow/underflow errors. ## Error Handling and Edge Cases - **Overflow/Underflow:** Ensure that the input int128 values do not exceed their range when packing into BeforeSwapDelta. - **Zero Values:** ZERO_DELTA represents no balance changes. Be cautious when interpreting zero values in specific contexts. - **Sign Mismatch:** Ensure that the signs of the delta values correctly represent the intended balance changes from the hook's perspective. ## Example Usage in a Hook ### Basic Example Here's a simple example of how `BeforeSwapDelta` might be used in a `beforeSwap` hook: ```solidity function _beforeSwap( address, PoolKey calldata, IPoolManager.SwapParams calldata params, bytes calldata ) internal override returns (int256 amountIn, BeforeSwapDelta delta, uint24) { // Convert the specified amount to int128 int128 specifiedAmount = params.amountSpecified.toInt128(); // In this example, we're not modifying the unspecified amount int128 unspecifiedAmount = 0; // Calculated based on your custom logic // Create the BeforeSwapDelta delta = toBeforeSwapDelta(specifiedAmount, unspecifiedAmount); // Return the original amount as amountIn amountIn = params.amountSpecified; // Return 0 for lpFeeOverride as we're not changing the LP fee return (amountIn, delta, 0); } ``` Let's break down what this basic hook is doing: 1. It converts the `params.amountSpecified` to `int128`, which is required for `BeforeSwapDelta`. 2. It sets the `unspecifiedAmount` to 0, which means this hook isn't modifying the counterpart token in the swap. 3. It creates a `BeforeSwapDelta` using these amounts. 4. It returns the original `amountIn`, the created `delta`, and 0 for `lpFeeOverride`. This basic example doesn't modify the swap parameters or introduce any fees. It demonstrates the minimal structure of a `beforeSwap` hook using `BeforeSwapDelta`. ## Advanced Example: Implementing a Fee For a more practical use case, here's an example that implements a simple fee mechanism: ```solidity function _beforeSwap( address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata ) internal override returns (int256 amountIn, BeforeSwapDelta delta, uint24) { // Determine if this is a swap for token0 or token1 bool zeroForOne = params.zeroForOne; // Convert the specified amount to int128, ensuring it's positive int128 specifiedAmount = params.amountSpecified.abs().toInt128(); // Calculate a 0.1% fee int128 fee = specifiedAmount / 1000; // Adjust the specified amount based on swap direction int128 adjustedSpecifiedAmount; if (params.exactInput) { // For exact input, reduce the amount by the fee adjustedSpecifiedAmount = specifiedAmount - fee; } else { // For exact output, increase the amount by the fee adjustedSpecifiedAmount = specifiedAmount + fee; } // Create the BeforeSwapDelta delta = zeroForOne ? BeforeSwapDelta.from(-adjustedSpecifiedAmount, 0) : BeforeSwapDelta.from(0, -adjustedSpecifiedAmount); // Return the original amount as amountIn amountIn = params.amountSpecified; // Return 0 for lpFeeOverride as we're not changing the LP fee return (amountIn, delta, 0); } ``` **Let's break down what this hook is doing:** 1. **Swap Direction Determination:** The hook checks `params.zeroForOne` to determine the direction of the swap (token0 to token1 or vice versa). 2. **Amount Conversion:** It converts `params.amountSpecified` to a positive `int128`. This is necessary because `BeforeSwapDelta` works with `int128` values. 3. **Fee Calculation:** A 0.1% fee is calculated based on the specified amount. 4. **Amount Adjustment:** Depending on whether the swap is exact input or exact output, the specified amount is adjusted: - For exact input, the fee is subtracted (user provides less to the pool). - For exact output, the fee is added (user needs to provide more to the pool). 5. **BeforeSwapDelta Creation:** The `BeforeSwapDelta` is created using the adjusted amount. The negative sign indicates that the pool will receive these tokens from the user. The amount is placed in either the first or second parameter of `BeforeSwapDelta.from()` depending on the swap direction. 6. **Return Values:** - `amountIn` is set to the original `params.amountSpecified`. This allows the pool to account for the full amount the user is putting in or expecting out. - The `delta` value contains our adjusted amounts. - `0` is returned for `lpFeeOverride`, meaning we're not changing the default LP fee. **What This Accomplishes:** - This hook implements a 0.1% fee on the swaps. - It handles both exact input and exact output swaps correctly. - It accounts for the swap direction (token0 to token1 or vice versa). - The fee is taken from the input amount for exact input swaps, or added to the input amount for exact output swaps. - The pool will see the full input/output amount, but will only swap the adjusted amount (after accounting for the fee). - The difference between the original amount and the adjusted amount effectively becomes the hook's fee. **Considerations:** - This example assumes the fee is always taken in the input token. In practice, you might want to design more sophisticated fee structures. - The hook doesn't handle storage of collected fees. In a real implementation, you'd need to account for and possibly transfer these fees. - Always ensure that your hook's logic is consistent with the overall pool behavior and doesn't introduce unexpected side effects. - This implementation doesn't change the LP fee (lpFeeOverride is 0). In some cases, you might want to adjust this as well. As you can see from this example, by using `BeforeSwapDelta`, hooks can implement custom logic such as fees, rebates, or other modifications to the swap parameters, allowing for highly flexible and customizable pool behavior in Uniswap V4. --- ## Currency Guide `Currency` is a custom type that represents either native currency (ETH) or ERC20 tokens. ## Type Definition ```solidity type Currency is address; ``` ## Global Functions ### equals ```solidity function equals(Currency currency, Currency other) pure returns (bool) ``` Checks if two `Currency` values are equal. | Param Name | Type | Description | |------------|----------|----------------------------| | currency | Currency | The first Currency value | | other | Currency | The second Currency value | Returns `true` if the Currency values are equal, `false` otherwise. ### greaterThan ```solidity function greaterThan(Currency currency, Currency other) pure returns (bool) ``` Compares two `Currency` values based on their underlying addresses. | Param Name | Type | Description | |------------|----------|----------------------------| | currency | Currency | The first Currency value | | other | Currency | The second Currency value | Returns `true` if the underlying address of `currency` is numerically greater than the underlying address of `other`, `false` otherwise. Note: This comparison is based on the numerical value of the addresses and does not imply any inherent ordering or value relationship between different currencies. It's primarily used for consistent ordering in data structures. ### lessThan ```solidity function lessThan(Currency currency, Currency other) pure returns (bool) ``` Compares two `Currency` values based on their underlying addresses. | Param Name | Type | Description | |------------|----------|----------------------------| | currency | Currency | The first Currency value | | other | Currency | The second Currency value | Returns `true` if the underlying address of `currency` is numerically less than the underlying address of `other`, `false` otherwise. Note: As with `greaterThan`, this comparison is based on address values and does not imply any inherent ordering or value relationship between currencies. ### greaterThanOrEqualTo ```solidity function greaterThanOrEqualTo(Currency currency, Currency other) pure returns (bool) ``` Checks if one `Currency` value is greater than or equal to another, based on their underlying addresses. | Param Name | Type | Description | |------------|----------|----------------------------| | currency | Currency | The first Currency value | | other | Currency | The second Currency value | Returns `true` if the underlying address of `currency` is numerically greater than or equal to the underlying address of `other`, `false` otherwise. ## CurrencyLibrary The `CurrencyLibrary` provides utility functions for handling both native currency (ETH) and ERC20 tokens. ### Constants ```solidity Currency public constant NATIVE = Currency.wrap(address(0)); ``` `NATIVE` represents the native currency (ETH). It is defined as a `Currency` with the underlying address of `address(0)`. ### Functions #### transfer ```solidity function transfer(Currency currency, address to, uint256 amount) internal ``` Transfers `amount` of `currency` to the `to` address. | Param Name | Type | Description | |------------|----------|---------------------------------------| | currency | Currency | The currency to transfer | | to | address | The recipient address | | amount | uint256 | The amount of currency to transfer | #### balanceOfSelf ```solidity function balanceOfSelf(Currency currency) internal view returns (uint256) ``` Returns the balance of `currency` held by the contract itself. | Param Name | Type | Description | |------------|----------|----------------------------| | currency | Currency | The currency to check | Returns the balance of the specified currency. #### balanceOf ```solidity function balanceOf(Currency currency, address owner) internal view returns (uint256) ``` Returns the balance of `currency` held by the `owner` address. | Param Name | Type | Description | |------------|----------|----------------------------| | currency | Currency | The currency to check | | owner | address | The address to check | Returns the balance of the specified currency for the given address. #### isNative ```solidity function isNative(Currency currency) internal pure returns (bool) ``` Checks if the given `currency` is the native currency (ETH). | Param Name | Type | Description | |------------|----------|----------------------------| | currency | Currency | The currency to check | Returns `true` if the currency is native (ETH), `false` otherwise. #### toId ```solidity function toId(Currency currency) internal pure returns (uint256) ``` Converts a `Currency` to its corresponding ID. | Param Name | Type | Description | |------------|----------|----------------------------| | currency | Currency | The currency to convert | Returns the ID of the currency. #### fromId ```solidity function fromId(uint256 id) internal pure returns (Currency) ``` Converts an ID to its corresponding `Currency`. | Param Name | Type | Description | |------------|----------|----------------------------| | id | uint256 | The ID to convert | Returns the Currency corresponding to the given ID. --- ## PoolKey Guide `PoolKey` is a crucial struct in Uniswap V4 that uniquely identifies a liquidity pool. It encapsulates all the essential parameters that define a pool's characteristics. ## Structure ```solidity struct PoolKey { Currency currency0; Currency currency1; uint24 fee; int24 tickSpacing; IHooks hooks; } ``` ## Fields | Field Name | Type | Description | |-------------|----------|----------------------------------------------------------------------------------------| | currency0 | Currency | The lower currency of the pool, sorted numerically | | currency1 | Currency | The higher currency of the pool, sorted numerically | | fee | uint24 | The pool swap fee, capped at 1,000,000. If the first bit is 1, the pool has a dynamic fee | | tickSpacing | int24 | The spacing between ticks for the pool | | hooks | IHooks | The address of the hooks contract associated with the pool | ## Important Notes - The `currency0` and `currency1` fields are always sorted numerically, with `currency0` being the lower value. This ensures consistent pool identification regardless of the order in which tokens are provided. - The `fee` field can represent either a static fee or indicate that the pool uses a dynamic fee mechanism. - The `tickSpacing` field determines the granularity of price ranges that can be used for liquidity provision. - The `hooks` field is an interface of our Hooks that the PoolManager uses to call these functions --- ## Custom Error Selectors These are custom error selectors for Uniswap v4 contracts. ## IPoolManager.sol | Error Selector | Hex Value | |-----------------------------------------------------|--------------| | `IPoolManager.CurrencyNotSettled.selector` | `0x5212cba1` | | `IPoolManager.PoolNotInitialized.selector` | `0x486aa307` | | `IPoolManager.AlreadyUnlocked.selector` | `0x5090d6c6` | | `IPoolManager.ManagerLocked.selector` | `0x54e3ca0d` | | `IPoolManager.TickSpacingTooLarge.selector` | `0xb02b5dc2` | | `IPoolManager.TickSpacingTooSmall.selector` | `0x16fe7696` | | `IPoolManager.CurrenciesOutOfOrderOrEqual.selector` | `0xeaa6c6eb` | | `IPoolManager.UnauthorizedDynamicLPFeeUpdate.selector` | `0x30d21641` | | `IPoolManager.SwapAmountCannotBeZero.selector` | `0xbe8b8507` | | `IPoolManager.NonZeroNativeValue.selector` | `0x19d245cf` | ## Hooks.sol | Error Selector | Hex Value | |-----------------------------------------------------|--------------| | `Hooks.HookAddressNotValid.selector` | `0xe65af6a0` | | `Hooks.InvalidHookResponse.selector` | `0x1e048e1d` | | `Hooks.FailedHookCall.selector` | `0x36bc48c5` | | `Hooks.HookDeltaExceedsSwapAmount.selector` | `0xfa0b71d6` | ## Pool.sol | Error Selector | Hex Value | |-----------------------------------------------------|--------------| | `Pool.TicksMisordered.selector` | `0xc4433ed5` | | `Pool.TickLowerOutOfBounds.selector` | `0xd5e2f7ab` | | `Pool.TickUpperOutOfBounds.selector` | `0x1ad777f8` | | `Pool.TickLiquidityOverflow.selector` | `0xb8e3c385` | | `Pool.TickNotInitialized.selector` | `0x82a774d3` | | `Pool.PoolAlreadyInitialized.selector` | `0x7983c051` | | `Pool.PoolNotInitialized.selector` | `0x486aa307` | | `Pool.PriceLimitAlreadyExceeded.selector` | `0x7c9c6e8f` | | `Pool.PriceLimitOutOfBounds.selector` | `0x9e4d7cc7` | | `Pool.NoLiquidityToReceiveFees.selector` | `0xa74f97ab` | | `Pool.InvalidFeeForExactOut.selector` | `0x96206246` | ## IProtocolFees.sol | Error Selector | Hex Value | |-----------------------------------------------------|--------------| | `IProtocolFees.ProtocolFeeCannotBeFetched.selector` | `0x1ee49702` | | `IProtocolFees.InvalidProtocolFee.selector` | `0xba97f838` | | `IProtocolFees.InvalidCaller.selector` | `0x48f5c3ed` | ## LPFeeLibrary.sol | Error Selector | Hex Value | |-----------------------------------------------------|--------------| | `LPFeeLibrary.FeeTooLarge.selector` | `0xfc5bee12` | ## Position.sol | Error Selector | Hex Value | |-----------------------------------------------------|----------------| | `Position.CannotUpdateEmptyPosition.selector` | `0xaefeb924` | ## Reserves.sol | Error Selector | Hex Value | |-----------------------------------------------------|----------------| | `Reserves.ReservesMustBeSynced.selector` | `0x8774be48` | ## SqrtPriceMath.sol | Error Selector | Hex Value | |-----------------------------------------------------|--------------| | `SqrtPriceMath.InvalidPriceOrLiquidity.selector` | `0x4f2461b8` | | `SqrtPriceMath.InvalidPrice.selector` | `0x00bfc921` | | `SqrtPriceMath.NotEnoughLiquidity.selector` | `0x4323a555` | | `SqrtPriceMath.PriceOverflow.selector` | `0xf5c787f1` | ## TickBitmap.sol | Error Selector | Hex Value | |-----------------------------------------------------|--------------| | `TickBitmap.TickMisaligned.selector` | `0xd4d8f3e6` | ## TickMath.sol | Error Selector | Hex Value | |-----------------------------------------------------|--------------| | `TickMath.InvalidTick.selector` | `0xce8ef7fc` | | `TickMath.InvalidSqrtPrice.selector` | `0x31efafe8` | --- ## PositionDescriptor [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/PositionDescriptor.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IPositionDescriptor](contracts/v4/reference/periphery/interfaces/IPositionDescriptor.md) Produces a string containing the data URI for a JSON metadata string ## State Variables ### DAI ```solidity address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; ``` ### USDC ```solidity address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; ``` ### USDT ```solidity address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; ``` ### TBTC ```solidity address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; ``` ### WBTC ```solidity address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; ``` ### wrappedNative ```solidity address public immutable wrappedNative; ``` ### nativeCurrencyLabelBytes ```solidity bytes32 private immutable nativeCurrencyLabelBytes; ``` ### poolManager ```solidity IPoolManager public immutable poolManager; ``` ## Functions ### constructor ```solidity constructor(IPoolManager _poolManager, address _wrappedNative, bytes32 _nativeCurrencyLabelBytes); ``` ### nativeCurrencyLabel Returns the native currency label as a string ```solidity function nativeCurrencyLabel() public view returns (string memory); ``` ### tokenURI Produces the URI describing a particular token ID *Note this URI may be a data: URI with the JSON contents directly inlined* ```solidity function tokenURI(IPositionManager positionManager, uint256 tokenId) external view override returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`positionManager`|`IPositionManager`|The position manager for which to describe the token| |`tokenId`|`uint256`|The ID of the token for which to produce a description, which may not be valid| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The URI of the ERC721-compliant metadata| ### flipRatio Returns true if currency0 has higher priority than currency1 ```solidity function flipRatio(address currency0, address currency1) public view returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency0`|`address`|The first currency address| |`currency1`|`address`|The second currency address| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|True if currency0 has higher priority than currency1| ### currencyRatioPriority Returns the priority of a currency. For certain currencies on mainnet, the smaller the currency, the higher the priority And those with the higher priority values (more positive values) will be in the numerator of the price ratio ```solidity function currencyRatioPriority(address currency) public view returns (int256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`address`|The currency address| **Returns** |Name|Type|Description| |----|----|-----------| |``|`int256`|The priority of the currency| --- ## PositionManager [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/PositionManager.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IPositionManager](contracts/v4/reference/periphery/interfaces/IPositionManager.md), [ERC721Permit_v4](contracts/v4/reference/periphery/base/ERC721Permit_v4.md), [PoolInitializer_v4](contracts/v4/reference/periphery/base/PoolInitializer_v4.md), [Multicall_v4](contracts/v4/reference/periphery/base/Multicall_v4.md), [DeltaResolver](contracts/v4/reference/periphery/base/DeltaResolver.md), [ReentrancyLock](contracts/v4/reference/periphery/base/ReentrancyLock.md), [BaseActionsRouter](contracts/v4/reference/periphery/base/BaseActionsRouter.md), [Notifier](contracts/v4/reference/periphery/base/Notifier.md), [Permit2Forwarder](contracts/v4/reference/periphery/base/Permit2Forwarder.md), [NativeWrapper](contracts/v4/reference/periphery/base/NativeWrapper.md) The PositionManager (PosM) contract is responsible for creating liquidity positions on v4. PosM mints and manages ERC721 tokens associated with each position. ## State Variables ### nextTokenId Used to get the ID that will be used for the next minted liquidity position *The ID of the next token that will be minted. Skips 0* ```solidity uint256 public nextTokenId = 1; ``` ### tokenDescriptor ```solidity IPositionDescriptor public immutable tokenDescriptor; ``` ### positionInfo ```solidity mapping(uint256 tokenId => PositionInfo info) public positionInfo; ``` ### poolKeys ```solidity mapping(bytes25 poolId => PoolKey poolKey) public poolKeys; ``` ## Functions ### constructor ```solidity constructor( IPoolManager _poolManager, IAllowanceTransfer _permit2, uint256 _unsubscribeGasLimit, IPositionDescriptor _tokenDescriptor, IWETH9 _weth9 ) BaseActionsRouter(_poolManager) Permit2Forwarder(_permit2) ERC721Permit_v4("Uniswap v4 Positions NFT", "UNI-V4-POSM") Notifier(_unsubscribeGasLimit) NativeWrapper(_weth9); ``` ### checkDeadline Reverts if the deadline has passed ```solidity modifier checkDeadline(uint256 deadline); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`deadline`|`uint256`|The timestamp at which the call is no longer valid, passed in by the caller| ### onlyIfApproved Reverts if the caller is not the owner or approved for the ERC721 token *either msg.sender or msgSender() is passed in as the caller msgSender() should ONLY be used if this is called from within the unlockCallback, unless the codepath has reentrancy protection* ```solidity modifier onlyIfApproved(address caller, uint256 tokenId) override; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`caller`|`address`|The address of the caller| |`tokenId`|`uint256`|the unique identifier of the ERC721 token| ### onlyIfPoolManagerLocked Enforces that the PoolManager is locked. ```solidity modifier onlyIfPoolManagerLocked() override; ``` ### tokenURI ```solidity function tokenURI(uint256 tokenId) public view override returns (string memory); ``` ### modifyLiquidities Unlocks Uniswap v4 PoolManager and batches actions for modifying liquidity *This is the standard entrypoint for the PositionManager* ```solidity function modifyLiquidities(bytes calldata unlockData, uint256 deadline) external payable isNotLocked checkDeadline(deadline); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`unlockData`|`bytes`|is an encoding of actions, and parameters for those actions| |`deadline`|`uint256`|is the deadline for the batched actions to be executed| ### modifyLiquiditiesWithoutUnlock Batches actions for modifying liquidity without unlocking v4 PoolManager *This must be called by a contract that has already unlocked the v4 PoolManager* ```solidity function modifyLiquiditiesWithoutUnlock(bytes calldata actions, bytes[] calldata params) external payable isNotLocked; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`actions`|`bytes`|the actions to perform| |`params`|`bytes[]`|the parameters to provide for the actions| ### msgSender function that returns address considered executor of the actions *The other context functions, _msgData and _msgValue, are not supported by this contract In many contracts this will be the address that calls the initial entry point that calls `_executeActions` `msg.sender` shouldn't be used, as this will be the v4 pool manager contract that calls `unlockCallback` If using ReentrancyLock.sol, this function can return _getLocker()* ```solidity function msgSender() public view override returns (address); ``` ### _handleAction ```solidity function _handleAction(uint256 action, bytes calldata params) internal virtual override; ``` ### _increase *Calling increase with 0 liquidity will credit the caller with any underlying fees of the position* ```solidity function _increase(uint256 tokenId, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData) internal onlyIfApproved(msgSender(), tokenId); ``` ### _increaseFromDeltas *The liquidity delta is derived from open deltas in the pool manager.* ```solidity function _increaseFromDeltas(uint256 tokenId, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData) internal onlyIfApproved(msgSender(), tokenId); ``` ### _decrease *Calling decrease with 0 liquidity will credit the caller with any underlying fees of the position* ```solidity function _decrease(uint256 tokenId, uint256 liquidity, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) internal onlyIfApproved(msgSender(), tokenId); ``` ### _mint ```solidity function _mint( PoolKey calldata poolKey, int24 tickLower, int24 tickUpper, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, address owner, bytes calldata hookData ) internal; ``` ### _mintFromDeltas ```solidity function _mintFromDeltas( PoolKey calldata poolKey, int24 tickLower, int24 tickUpper, uint128 amount0Max, uint128 amount1Max, address owner, bytes calldata hookData ) internal; ``` ### _burn *this is overloaded with ERC721Permit_v4._burn* ```solidity function _burn(uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) internal onlyIfApproved(msgSender(), tokenId); ``` ### _settlePair ```solidity function _settlePair(Currency currency0, Currency currency1) internal; ``` ### _takePair ```solidity function _takePair(Currency currency0, Currency currency1, address recipient) internal; ``` ### _close ```solidity function _close(Currency currency) internal; ``` ### _clearOrTake *integrators may elect to forfeit positive deltas with clear if the forfeit amount exceeds the user-specified max, the amount is taken instead if there is no credit, no call is made.* ```solidity function _clearOrTake(Currency currency, uint256 amountMax) internal; ``` ### _sweep Sweeps the entire contract balance of specified currency to the recipient ```solidity function _sweep(Currency currency, address to) internal; ``` ### _modifyLiquidity *if there is a subscriber attached to the position, this function will notify the subscriber* ```solidity function _modifyLiquidity( PositionInfo info, PoolKey memory poolKey, int256 liquidityChange, bytes32 salt, bytes calldata hookData ) internal returns (BalanceDelta liquidityDelta, BalanceDelta feesAccrued); ``` ### _pay ```solidity function _pay(Currency currency, address payer, uint256 amount) internal override; ``` ### _setSubscribed an internal helper used by Notifier ```solidity function _setSubscribed(uint256 tokenId) internal override; ``` ### _setUnsubscribed an internal helper used by Notifier ```solidity function _setUnsubscribed(uint256 tokenId) internal override; ``` ### transferFrom *overrides solmate transferFrom in case a notification to subscribers is needed* *will revert if pool manager is locked* ```solidity function transferFrom(address from, address to, uint256 id) public virtual override onlyIfPoolManagerLocked; ``` ### getPoolAndPositionInfo Returns the pool key and position info of a position ```solidity function getPoolAndPositionInfo(uint256 tokenId) public view returns (PoolKey memory poolKey, PositionInfo info); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| **Returns** |Name|Type|Description| |----|----|-----------| |`poolKey`|`PoolKey`|the pool key of the position| |`info`|`PositionInfo`|a uint256 packed value holding information about the position including the range (tickLower, tickUpper)| ### getPositionLiquidity Returns the liquidity of a position *this value can be processed as an amount0 and amount1 by using the LiquidityAmounts library* ```solidity function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|the position's liquidity, as a liquidityAmount| ### _getLiquidity ```solidity function _getLiquidity(uint256 tokenId, PoolKey memory poolKey, int24 tickLower, int24 tickUpper) internal view returns (uint128 liquidity); ``` --- ## UniswapV4DeployerCompetition [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/UniswapV4DeployerCompetition.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IUniswapV4DeployerCompetition](contracts/v4/reference/periphery/interfaces/IUniswapV4DeployerCompetition.md) A contract to crowdsource a salt for the best Uniswap V4 address ## State Variables ### bestAddressSalt *The salt for the best address found so far* ```solidity bytes32 public bestAddressSalt; ``` ### bestAddressSubmitter *The submitter of the best address found so far* ```solidity address public bestAddressSubmitter; ``` ### competitionDeadline *The deadline for the competition* ```solidity uint256 public immutable competitionDeadline; ``` ### initCodeHash *The init code hash of the V4 contract* ```solidity bytes32 public immutable initCodeHash; ``` ### deployer *The deployer who can initiate the deployment of the v4 PoolManager, until the exclusive deploy deadline.* *After this deadline anyone can deploy.* ```solidity address public immutable deployer; ``` ### exclusiveDeployDeadline *The deadline for exclusive deployment by deployer after deadline* ```solidity uint256 public immutable exclusiveDeployDeadline; ``` ## Functions ### constructor ```solidity constructor( bytes32 _initCodeHash, uint256 _competitionDeadline, address _exclusiveDeployer, uint256 _exclusiveDeployLength ); ``` ### updateBestAddress Updates the best address if the new address has a better vanity score *The first 20 bytes of the salt must be either address(0) or msg.sender* ```solidity function updateBestAddress(bytes32 salt) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`salt`|`bytes32`|The salt to use to compute the new address with CREATE2| ### deploy deploys the Uniswap v4 PoolManager contract *The bytecode must match the initCodeHash* ```solidity function deploy(bytes memory bytecode) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`bytecode`|`bytes`|The bytecode of the Uniswap v4 PoolManager contract| ### bestAddress *returns the best address found so far* ```solidity function bestAddress() public view returns (address); ``` --- ## V4Router [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/V4Router.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IV4Router](contracts/v4/reference/periphery/interfaces/IV4Router.md), [BaseActionsRouter](contracts/v4/reference/periphery/base/BaseActionsRouter.md), [DeltaResolver](contracts/v4/reference/periphery/base/DeltaResolver.md) Abstract contract that contains all internal logic needed for routing through Uniswap v4 pools *the entry point to executing actions in this contract is calling `BaseActionsRouter._executeActions` An inheriting contract should call _executeActions at the point that they wish actions to be executed* ## Functions ### constructor ```solidity constructor(IPoolManager _poolManager) BaseActionsRouter(_poolManager); ``` ### _handleAction ```solidity function _handleAction(uint256 action, bytes calldata params) internal override; ``` ### _swapExactInputSingle ```solidity function _swapExactInputSingle(IV4Router.ExactInputSingleParams calldata params) private; ``` ### _swapExactInput ```solidity function _swapExactInput(IV4Router.ExactInputParams calldata params) private; ``` ### _swapExactOutputSingle ```solidity function _swapExactOutputSingle(IV4Router.ExactOutputSingleParams calldata params) private; ``` ### _swapExactOutput ```solidity function _swapExactOutput(IV4Router.ExactOutputParams calldata params) private; ``` ### _swap ```solidity function _swap(PoolKey memory poolKey, bool zeroForOne, int256 amountSpecified, bytes calldata hookData) private returns (int128 reciprocalAmount); ``` --- ## BaseActionsRouter [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/BaseActionsRouter.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [SafeCallback](contracts/v4/reference/periphery/base/SafeCallback.md) Abstract contract for performing a combination of actions on Uniswap v4. *Suggested uint256 action values are defined in Actions.sol, however any definition can be used* ## Functions ### constructor ```solidity constructor(IPoolManager _poolManager) SafeCallback(_poolManager); ``` ### _executeActions internal function that triggers the execution of a set of actions on v4 *inheriting contracts should call this function to trigger execution* ```solidity function _executeActions(bytes calldata unlockData) internal; ``` ### _unlockCallback function that is called by the PoolManager through the SafeCallback.unlockCallback ```solidity function _unlockCallback(bytes calldata data) internal override returns (bytes memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`data`|`bytes`|Abi encoding of (bytes actions, bytes[] params) where params[i] is the encoded parameters for actions[i]| ### _executeActionsWithoutUnlock ```solidity function _executeActionsWithoutUnlock(bytes calldata actions, bytes[] calldata params) internal; ``` ### _handleAction function to handle the parsing and execution of an action and its parameters ```solidity function _handleAction(uint256 action, bytes calldata params) internal virtual; ``` ### msgSender function that returns address considered executor of the actions *The other context functions, _msgData and _msgValue, are not supported by this contract In many contracts this will be the address that calls the initial entry point that calls `_executeActions` `msg.sender` shouldn't be used, as this will be the v4 pool manager contract that calls `unlockCallback` If using ReentrancyLock.sol, this function can return _getLocker()* ```solidity function msgSender() public view virtual returns (address); ``` ### _mapRecipient Calculates the address for a action ```solidity function _mapRecipient(address recipient) internal view returns (address); ``` ### _mapPayer Calculates the payer for an action ```solidity function _mapPayer(bool payerIsUser) internal view returns (address); ``` ## Errors ### InputLengthMismatch emitted when different numbers of parameters and actions are provided ```solidity error InputLengthMismatch(); ``` ### UnsupportedAction emitted when an inheriting contract does not support an action ```solidity error UnsupportedAction(uint256 action); ``` --- ## BaseV4Quoter [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/BaseV4Quoter.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [SafeCallback](contracts/v4/reference/periphery/base/SafeCallback.md) ## Functions ### constructor ```solidity constructor(IPoolManager _poolManager) SafeCallback(_poolManager); ``` ### selfOnly *Only this address may call this function. Used to mimic internal functions, using an external call to catch and parse revert reasons* ```solidity modifier selfOnly(); ``` ### _unlockCallback ```solidity function _unlockCallback(bytes calldata data) internal override returns (bytes memory); ``` ### _swap if amountSpecified < 0, the swap is exactInput, otherwise exactOutput *Execute a swap and return the balance delta* ```solidity function _swap(PoolKey memory poolKey, bool zeroForOne, int256 amountSpecified, bytes calldata hookData) internal returns (BalanceDelta swapDelta); ``` ## Errors ### NotEnoughLiquidity ```solidity error NotEnoughLiquidity(PoolId poolId); ``` ### NotSelf ```solidity error NotSelf(); ``` ### UnexpectedCallSuccess ```solidity error UnexpectedCallSuccess(); ``` --- ## DeltaResolver [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/DeltaResolver.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [ImmutableState](contracts/v4/reference/periphery/base/ImmutableState.md) Abstract contract used to sync, send, and settle funds to the pool manager *Note that sync() is called before any erc-20 transfer in `settle`.* ## Functions ### _take Take an amount of currency out of the PoolManager *Returns early if the amount is 0* ```solidity function _take(Currency currency, address recipient, uint256 amount) internal; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`Currency`|Currency to take| |`recipient`|`address`|Address to receive the currency| |`amount`|`uint256`|Amount to take| ### _settle Pay and settle a currency to the PoolManager *The implementing contract must ensure that the `payer` is a secure address* *Returns early if the amount is 0* ```solidity function _settle(Currency currency, address payer, uint256 amount) internal; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`Currency`|Currency to settle| |`payer`|`address`|Address of the payer| |`amount`|`uint256`|Amount to send| ### _pay Abstract function for contracts to implement paying tokens to the poolManager *The recipient of the payment should be the poolManager* ```solidity function _pay(Currency token, address payer, uint256 amount) internal virtual; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`token`|`Currency`|The token to settle. This is known not to be the native currency| |`payer`|`address`|The address who should pay tokens| |`amount`|`uint256`|The number of tokens to send| ### _getFullDebt Obtain the full amount owed by this contract (negative delta) ```solidity function _getFullDebt(Currency currency) internal view returns (uint256 amount); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`Currency`|Currency to get the delta for| **Returns** |Name|Type|Description| |----|----|-----------| |`amount`|`uint256`|The amount owed by this contract as a uint256| ### _getFullCredit Obtain the full credit owed to this contract (positive delta) ```solidity function _getFullCredit(Currency currency) internal view returns (uint256 amount); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`Currency`|Currency to get the delta for| **Returns** |Name|Type|Description| |----|----|-----------| |`amount`|`uint256`|The amount owed to this contract as a uint256| ### _mapSettleAmount Calculates the amount for a settle action ```solidity function _mapSettleAmount(uint256 amount, Currency currency) internal view returns (uint256); ``` ### _mapTakeAmount Calculates the amount for a take action ```solidity function _mapTakeAmount(uint256 amount, Currency currency) internal view returns (uint256); ``` ### _mapWrapUnwrapAmount Calculates the sanitized amount before wrapping/unwrapping. ```solidity function _mapWrapUnwrapAmount(Currency inputCurrency, uint256 amount, Currency outputCurrency) internal view returns (uint256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`inputCurrency`|`Currency`|The currency, either native or wrapped native, that this contract holds| |`amount`|`uint256`|The amount to wrap or unwrap. Can be CONTRACT_BALANCE, OPEN_DELTA or a specific amount| |`outputCurrency`|`Currency`|The currency after the wrap/unwrap that the user may owe a balance in on the poolManager| ## Errors ### DeltaNotPositive Emitted trying to settle a positive delta. ```solidity error DeltaNotPositive(Currency currency); ``` ### DeltaNotNegative Emitted trying to take a negative delta. ```solidity error DeltaNotNegative(Currency currency); ``` ### InsufficientBalance Emitted when the contract does not have enough balance to wrap or unwrap. ```solidity error InsufficientBalance(); ``` --- ## EIP712_v4 [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/EIP712_v4.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IEIP712_v4](contracts/v4/reference/periphery/interfaces/IEIP712_v4.md) Generic EIP712 implementation *Maintains cross-chain replay protection in the event of a fork* *Should not be delegatecall'd because DOMAIN_SEPARATOR returns the cached hash and does not recompute with the delegatecallers address* *Reference: https://github.com/Uniswap/permit2/blob/3f17e8db813189a03950dc7fc8382524a095c053/src/EIP712.sol* *Reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7bd2b2aaf68c21277097166a9a51eb72ae239b34/contracts/utils/cryptography/EIP712.sol* ## State Variables ### _CACHED_DOMAIN_SEPARATOR ```solidity bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; ``` ### _CACHED_CHAIN_ID ```solidity uint256 private immutable _CACHED_CHAIN_ID; ``` ### _HASHED_NAME ```solidity bytes32 private immutable _HASHED_NAME; ``` ### _TYPE_HASH ```solidity bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); ``` ## Functions ### constructor ```solidity constructor(string memory name); ``` ### DOMAIN_SEPARATOR Returns the domain separator for the current chain. ```solidity function DOMAIN_SEPARATOR() public view returns (bytes32); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes32`|bytes32 The domain separator| ### _buildDomainSeparator Builds a domain separator using the current chainId and contract address. ```solidity function _buildDomainSeparator() private view returns (bytes32); ``` ### _hashTypedData Creates an EIP-712 typed data hash ```solidity function _hashTypedData(bytes32 dataHash) internal view returns (bytes32 digest); ``` --- ## ERC721Permit_v4 [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/ERC721Permit_v4.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** ERC721, [IERC721Permit_v4](contracts/v4/reference/periphery/interfaces/IERC721Permit_v4.md), [EIP712_v4](contracts/v4/reference/periphery/base/EIP712_v4.md), [UnorderedNonce](contracts/v4/reference/periphery/base/UnorderedNonce.md) Nonfungible tokens that support an approve via signature, i.e. permit ## Functions ### constructor Computes the nameHash and versionHash ```solidity constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) EIP712_v4(name_); ``` ### checkSignatureDeadline Checks if the block's timestamp is before a signature's deadline ```solidity modifier checkSignatureDeadline(uint256 deadline); ``` ### permit Approve of a specific token ID for spending by spender via signature *payable so it can be multicalled with NATIVE related actions* ```solidity function permit(address spender, uint256 tokenId, uint256 deadline, uint256 nonce, bytes calldata signature) external payable checkSignatureDeadline(deadline); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`spender`|`address`|The account that is being approved| |`tokenId`|`uint256`|The ID of the token that is being approved for spending| |`deadline`|`uint256`|The deadline timestamp by which the call must be mined for the approve to work| |`nonce`|`uint256`|a unique value, for an owner, to prevent replay attacks; an unordered nonce where the top 248 bits correspond to a word and the bottom 8 bits calculate the bit position of the word| |`signature`|`bytes`|Concatenated data from a valid secp256k1 signature from the holder, i.e. abi.encodePacked(r, s, v)| ### permitForAll Set an operator with full permission to an owner's tokens via signature *payable so it can be multicalled with NATIVE related actions* ```solidity function permitForAll( address owner, address operator, bool approved, uint256 deadline, uint256 nonce, bytes calldata signature ) external payable checkSignatureDeadline(deadline); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|The address that is setting the operator| |`operator`|`address`|The address that will be set as an operator for the owner| |`approved`|`bool`|The permission to set on the operator| |`deadline`|`uint256`|The deadline timestamp by which the call must be mined for the approve to work| |`nonce`|`uint256`|a unique value, for an owner, to prevent replay attacks; an unordered nonce where the top 248 bits correspond to a word and the bottom 8 bits calculate the bit position of the word| |`signature`|`bytes`|Concatenated data from a valid secp256k1 signature from the holder, i.e. abi.encodePacked(r, s, v)| ### setApprovalForAll Enable or disable approval for a third party ("operator") to manage all of `msg.sender`'s assets *Emits the ApprovalForAll event. The contract MUST allow multiple operators per owner.* *Override Solmate's ERC721 setApprovalForAll so setApprovalForAll() and permit() share the _approveForAll method* ```solidity function setApprovalForAll(address operator, bool approved) public override; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`operator`|`address`|Address to add to the set of authorized operators| |`approved`|`bool`|True if the operator is approved, false to revoke approval| ### _approveForAll ```solidity function _approveForAll(address owner, address operator, bool approved) internal; ``` ### approve Change or reaffirm the approved address for an NFT *override Solmate's ERC721 approve so approve() and permit() share the _approve method Passing a spender address of zero can be used to remove any outstanding approvals Throws error unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.* ```solidity function approve(address spender, uint256 id) public override; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`spender`|`address`|The new approved NFT controller| |`id`|`uint256`|The tokenId of the NFT to approve| ### _approve ```solidity function _approve(address owner, address spender, uint256 id) internal; ``` ### _isApprovedOrOwner ```solidity function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool); ``` --- ## ImmutableState [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/ImmutableState.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IImmutableState](contracts/v4/reference/periphery/interfaces/IImmutableState.md) A collection of immutable state variables, commonly used across multiple contracts ## State Variables ### poolManager The Uniswap v4 PoolManager contract ```solidity IPoolManager public immutable poolManager; ``` ## Functions ### onlyPoolManager Only allow calls from the PoolManager contract ```solidity modifier onlyPoolManager(); ``` ### constructor ```solidity constructor(IPoolManager _poolManager); ``` ## Errors ### NotPoolManager Thrown when the caller is not PoolManager ```solidity error NotPoolManager(); ``` --- ## Multicall_v4 [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/Multicall_v4.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IMulticall_v4](contracts/v4/reference/periphery/interfaces/IMulticall_v4.md) Enables calling multiple methods in a single call to the contract ## Functions ### multicall Call multiple functions in the current contract and return the data from all of them if they all succeed *The `msg.value` is passed onto all subcalls, even if a previous subcall has consumed the ether. Subcalls can instead use `address(this).value` to see the available ETH, and consume it using \{value: x\}.* ```solidity function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`data`|`bytes[]`|The encoded function data for each of the calls to make to this contract| **Returns** |Name|Type|Description| |----|----|-----------| |`results`|`bytes[]`|The results from each of the calls passed in via data| --- ## NativeWrapper [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/NativeWrapper.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [ImmutableState](contracts/v4/reference/periphery/base/ImmutableState.md) Used for wrapping and unwrapping native ## State Variables ### WETH9 The address for WETH9 ```solidity IWETH9 public immutable WETH9; ``` ## Functions ### constructor ```solidity constructor(IWETH9 _weth9); ``` ### _wrap *The amount should already be \<= the current balance in this contract.* ```solidity function _wrap(uint256 amount) internal; ``` ### _unwrap *The amount should already be \<= the current balance in this contract.* ```solidity function _unwrap(uint256 amount) internal; ``` ### receive ```solidity receive() external payable; ``` ## Errors ### InvalidEthSender Thrown when an unexpected address sends ETH to this contract ```solidity error InvalidEthSender(); ``` --- ## Notifier [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/Notifier.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [INotifier](contracts/v4/reference/periphery/interfaces/INotifier.md) Notifier is used to opt in to sending updates to external contracts about position modifications or transfers ## State Variables ### NO_SUBSCRIBER ```solidity ISubscriber private constant NO_SUBSCRIBER = ISubscriber(address(0)); ``` ### unsubscribeGasLimit Returns and determines the maximum allowable gas-used for notifying unsubscribe ```solidity uint256 public immutable unsubscribeGasLimit; ``` ### subscriber Returns the subscriber for a respective position ```solidity mapping(uint256 tokenId => ISubscriber subscriber) public subscriber; ``` ## Functions ### constructor ```solidity constructor(uint256 _unsubscribeGasLimit); ``` ### onlyIfApproved Only allow callers that are approved as spenders or operators of the tokenId *to be implemented by the parent contract (PositionManager)* ```solidity modifier onlyIfApproved(address caller, uint256 tokenId) virtual; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`caller`|`address`|the address of the caller| |`tokenId`|`uint256`|the tokenId of the position| ### onlyIfPoolManagerLocked Enforces that the PoolManager is locked. ```solidity modifier onlyIfPoolManagerLocked() virtual; ``` ### _setUnsubscribed ```solidity function _setUnsubscribed(uint256 tokenId) internal virtual; ``` ### _setSubscribed ```solidity function _setSubscribed(uint256 tokenId) internal virtual; ``` ### subscribe Enables the subscriber to receive notifications for a respective position *Calling subscribe when a position is already subscribed will revert* ```solidity function subscribe(uint256 tokenId, address newSubscriber, bytes calldata data) external payable onlyIfPoolManagerLocked onlyIfApproved(msg.sender, tokenId); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| |`newSubscriber`|`address`|the address of the subscriber contract| |`data`|`bytes`|caller-provided data that's forwarded to the subscriber contract| ### unsubscribe Removes the subscriber from receiving notifications for a respective position *Callers must specify a high gas limit (remaining gas should be higher than unsubscriberGasLimit) such that the subscriber can be notified* ```solidity function unsubscribe(uint256 tokenId) external payable onlyIfPoolManagerLocked onlyIfApproved(msg.sender, tokenId); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| ### _unsubscribe ```solidity function _unsubscribe(uint256 tokenId) internal; ``` ### _removeSubscriberAndNotifyBurn *note this function also deletes the subscriber address from the mapping* ```solidity function _removeSubscriberAndNotifyBurn( uint256 tokenId, address owner, PositionInfo info, uint256 liquidity, BalanceDelta feesAccrued ) internal; ``` ### _notifyModifyLiquidity ```solidity function _notifyModifyLiquidity(uint256 tokenId, int256 liquidityChange, BalanceDelta feesAccrued) internal; ``` ### _call ```solidity function _call(address target, bytes memory encodedCall) internal returns (bool success); ``` --- ## Permit2Forwarder [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/Permit2Forwarder.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IPermit2Forwarder](contracts/v4/reference/periphery/interfaces/IPermit2Forwarder.md) Permit2Forwarder allows permitting this contract as a spender on permit2 *This contract does not enforce the spender to be this contract, but that is the intended use case* ## State Variables ### permit2 the Permit2 contract to forward approvals ```solidity IAllowanceTransfer public immutable permit2; ``` ## Functions ### constructor ```solidity constructor(IAllowanceTransfer _permit2); ``` ### permit allows forwarding a single permit to permit2 *this function is payable to allow multicall with NATIVE based actions* ```solidity function permit(address owner, IAllowanceTransfer.PermitSingle calldata permitSingle, bytes calldata signature) external payable returns (bytes memory err); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|the owner of the tokens| |`permitSingle`|`IAllowanceTransfer.PermitSingle`|the permit data| |`signature`|`bytes`|the signature of the permit; abi.encodePacked(r, s, v)| **Returns** |Name|Type|Description| |----|----|-----------| |`err`|`bytes`|the error returned by a reverting permit call, empty if successful| ### permitBatch allows forwarding batch permits to permit2 *this function is payable to allow multicall with NATIVE based actions* ```solidity function permitBatch(address owner, IAllowanceTransfer.PermitBatch calldata _permitBatch, bytes calldata signature) external payable returns (bytes memory err); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|the owner of the tokens| |`_permitBatch`|`IAllowanceTransfer.PermitBatch`|a batch of approvals| |`signature`|`bytes`|the signature of the permit; abi.encodePacked(r, s, v)| **Returns** |Name|Type|Description| |----|----|-----------| |`err`|`bytes`|the error returned by a reverting permit call, empty if successful| --- ## PoolInitializer_v4 [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/PoolInitializer_v4.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [ImmutableState](contracts/v4/reference/periphery/base/ImmutableState.md), [IPoolInitializer_v4](contracts/v4/reference/periphery/interfaces/IPoolInitializer_v4.md) Initializes a Uniswap v4 Pool *Enables create pool + mint liquidity in a single transaction with multicall* ## Functions ### initializePool Initialize a Uniswap v4 Pool *If the pool is already initialized, this function will not revert and just return type(int24).max* ```solidity function initializePool(PoolKey calldata key, uint160 sqrtPriceX96) external payable returns (int24); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The PoolKey of the pool to initialize| |`sqrtPriceX96`|`uint160`|The initial starting price of the pool, expressed as a sqrtPriceX96| **Returns** |Name|Type|Description| |----|----|-----------| |``|`int24`|The current tick of the pool, or type(int24).max if the pool creation failed, or the pool already existed| --- ## ReentrancyLock [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/ReentrancyLock.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) A transient reentrancy lock, that stores the caller's address as the lock ## Functions ### isNotLocked ```solidity modifier isNotLocked(); ``` ### _getLocker ```solidity function _getLocker() internal view returns (address); ``` ## Errors ### ContractLocked ```solidity error ContractLocked(); ``` --- ## SafeCallback [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/SafeCallback.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [ImmutableState](contracts/v4/reference/periphery/base/ImmutableState.md), IUnlockCallback A contract that only allows the Uniswap v4 PoolManager to call the unlockCallback ## Functions ### constructor ```solidity constructor(IPoolManager _poolManager) ImmutableState(_poolManager); ``` ### unlockCallback Called by the pool manager on `msg.sender` when the manager is unlocked *We force the onlyPoolManager modifier by exposing a virtual function after the onlyPoolManager check.* ```solidity function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`data`|`bytes`|The data that was passed to the call to unlock| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes`|Any data that you want to be returned from the unlock call| ### _unlockCallback *to be implemented by the child contract, to safely guarantee the logic is only executed by the PoolManager* ```solidity function _unlockCallback(bytes calldata data) internal virtual returns (bytes memory); ``` --- ## UnorderedNonce [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/base/UnorderedNonce.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IUnorderedNonce](contracts/v4/reference/periphery/interfaces/IUnorderedNonce.md) Contract state and methods for using unordered nonces in signatures ## State Variables ### nonces mapping of nonces consumed by each address, where a nonce is a single bit on the 256-bit bitmap *word is at most type(uint248).max* ```solidity mapping(address owner => mapping(uint256 word => uint256 bitmap)) public nonces; ``` ## Functions ### _useUnorderedNonce Consume a nonce, reverting if it has already been used ```solidity function _useUnorderedNonce(address owner, uint256 nonce) internal; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|address, the owner/signer of the nonce| |`nonce`|`uint256`|uint256, the nonce to consume. The top 248 bits are the word, the bottom 8 bits indicate the bit position| ### revokeNonce Revoke a nonce by spending it, preventing it from being used again *Used in cases where a valid nonce has not been broadcasted onchain, and the owner wants to revoke the validity of the nonce* ```solidity function revokeNonce(uint256 nonce) external payable; ``` --- ## IEIP712_v4 [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IEIP712_v4.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the EIP712 contract ## Functions ### DOMAIN_SEPARATOR Returns the domain separator for the current chain. ```solidity function DOMAIN_SEPARATOR() external view returns (bytes32); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes32`|bytes32 The domain separator| --- ## IERC721Permit_v4 [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IERC721Permit_v4.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the ERC721Permit_v4 contract ## Functions ### permit Approve of a specific token ID for spending by spender via signature *payable so it can be multicalled with NATIVE related actions* ```solidity function permit(address spender, uint256 tokenId, uint256 deadline, uint256 nonce, bytes calldata signature) external payable; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`spender`|`address`|The account that is being approved| |`tokenId`|`uint256`|The ID of the token that is being approved for spending| |`deadline`|`uint256`|The deadline timestamp by which the call must be mined for the approve to work| |`nonce`|`uint256`|a unique value, for an owner, to prevent replay attacks; an unordered nonce where the top 248 bits correspond to a word and the bottom 8 bits calculate the bit position of the word| |`signature`|`bytes`|Concatenated data from a valid secp256k1 signature from the holder, i.e. abi.encodePacked(r, s, v)| ### permitForAll Set an operator with full permission to an owner's tokens via signature *payable so it can be multicalled with NATIVE related actions* ```solidity function permitForAll( address owner, address operator, bool approved, uint256 deadline, uint256 nonce, bytes calldata signature ) external payable; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|The address that is setting the operator| |`operator`|`address`|The address that will be set as an operator for the owner| |`approved`|`bool`|The permission to set on the operator| |`deadline`|`uint256`|The deadline timestamp by which the call must be mined for the approve to work| |`nonce`|`uint256`|a unique value, for an owner, to prevent replay attacks; an unordered nonce where the top 248 bits correspond to a word and the bottom 8 bits calculate the bit position of the word| |`signature`|`bytes`|Concatenated data from a valid secp256k1 signature from the holder, i.e. abi.encodePacked(r, s, v)| ## Errors ### SignatureDeadlineExpired ```solidity error SignatureDeadlineExpired(); ``` ### NoSelfPermit ```solidity error NoSelfPermit(); ``` ### Unauthorized ```solidity error Unauthorized(); ``` --- ## IImmutableState [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IImmutableState.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the ImmutableState contract ## Functions ### poolManager The Uniswap v4 PoolManager contract ```solidity function poolManager() external view returns (IPoolManager); ``` --- ## IMulticall_v4 [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IMulticall_v4.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the Multicall_v4 contract ## Functions ### multicall Call multiple functions in the current contract and return the data from all of them if they all succeed *The `msg.value` is passed onto all subcalls, even if a previous subcall has consumed the ether. Subcalls can instead use `address(this).value` to see the available ETH, and consume it using \{value: x\}.* ```solidity function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`data`|`bytes[]`|The encoded function data for each of the calls to make to this contract| **Returns** |Name|Type|Description| |----|----|-----------| |`results`|`bytes[]`|The results from each of the calls passed in via data| --- ## INotifier [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/INotifier.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the Notifier contract ## Functions ### subscriber Returns the subscriber for a respective position ```solidity function subscriber(uint256 tokenId) external view returns (ISubscriber subscriber); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| **Returns** |Name|Type|Description| |----|----|-----------| |`subscriber`|`ISubscriber`|the subscriber contract| ### subscribe Enables the subscriber to receive notifications for a respective position *Calling subscribe when a position is already subscribed will revert* *payable so it can be multicalled with NATIVE related actions* *will revert if pool manager is locked* ```solidity function subscribe(uint256 tokenId, address newSubscriber, bytes calldata data) external payable; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| |`newSubscriber`|`address`|the address of the subscriber contract| |`data`|`bytes`|caller-provided data that's forwarded to the subscriber contract| ### unsubscribe Removes the subscriber from receiving notifications for a respective position *Callers must specify a high gas limit (remaining gas should be higher than unsubscriberGasLimit) such that the subscriber can be notified* *payable so it can be multicalled with NATIVE related actions* *Must always allow a user to unsubscribe. In the case of a malicious subscriber, a user can always unsubscribe safely, ensuring liquidity is always modifiable.* *will revert if pool manager is locked* ```solidity function unsubscribe(uint256 tokenId) external payable; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| ### unsubscribeGasLimit Returns and determines the maximum allowable gas-used for notifying unsubscribe ```solidity function unsubscribeGasLimit() external view returns (uint256); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|uint256 the maximum gas limit when notifying a subscriber's `notifyUnsubscribe` function| ## Events ### Subscription Emitted on a successful call to subscribe ```solidity event Subscription(uint256 indexed tokenId, address indexed subscriber); ``` ### Unsubscription Emitted on a successful call to unsubscribe ```solidity event Unsubscription(uint256 indexed tokenId, address indexed subscriber); ``` ## Errors ### NotSubscribed Thrown when unsubscribing without a subscriber ```solidity error NotSubscribed(); ``` ### NoCodeSubscriber Thrown when a subscriber does not have code ```solidity error NoCodeSubscriber(); ``` ### GasLimitTooLow Thrown when a user specifies a gas limit too low to avoid valid unsubscribe notifications ```solidity error GasLimitTooLow(); ``` ### SubscriptionReverted Wraps the revert message of the subscriber contract on a reverting subscription ```solidity error SubscriptionReverted(address subscriber, bytes reason); ``` ### ModifyLiquidityNotificationReverted Wraps the revert message of the subscriber contract on a reverting modify liquidity notification ```solidity error ModifyLiquidityNotificationReverted(address subscriber, bytes reason); ``` ### BurnNotificationReverted Wraps the revert message of the subscriber contract on a reverting burn notification ```solidity error BurnNotificationReverted(address subscriber, bytes reason); ``` ### AlreadySubscribed Thrown when a tokenId already has a subscriber ```solidity error AlreadySubscribed(uint256 tokenId, address subscriber); ``` --- ## IPermit2Forwarder [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IPermit2Forwarder.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the Permit2Forwarder contract ## Functions ### permit allows forwarding a single permit to permit2 *this function is payable to allow multicall with NATIVE based actions* ```solidity function permit(address owner, IAllowanceTransfer.PermitSingle calldata permitSingle, bytes calldata signature) external payable returns (bytes memory err); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|the owner of the tokens| |`permitSingle`|`IAllowanceTransfer.PermitSingle`|the permit data| |`signature`|`bytes`|the signature of the permit; abi.encodePacked(r, s, v)| **Returns** |Name|Type|Description| |----|----|-----------| |`err`|`bytes`|the error returned by a reverting permit call, empty if successful| ### permitBatch allows forwarding batch permits to permit2 *this function is payable to allow multicall with NATIVE based actions* ```solidity function permitBatch(address owner, IAllowanceTransfer.PermitBatch calldata _permitBatch, bytes calldata signature) external payable returns (bytes memory err); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`owner`|`address`|the owner of the tokens| |`_permitBatch`|`IAllowanceTransfer.PermitBatch`|a batch of approvals| |`signature`|`bytes`|the signature of the permit; abi.encodePacked(r, s, v)| **Returns** |Name|Type|Description| |----|----|-----------| |`err`|`bytes`|the error returned by a reverting permit call, empty if successful| --- ## IPoolInitializer_v4 [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IPoolInitializer_v4.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the PoolInitializer_v4 contract ## Functions ### initializePool Initialize a Uniswap v4 Pool *If the pool is already initialized, this function will not revert and just return type(int24).max* ```solidity function initializePool(PoolKey calldata key, uint160 sqrtPriceX96) external payable returns (int24); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`key`|`PoolKey`|The PoolKey of the pool to initialize| |`sqrtPriceX96`|`uint160`|The initial starting price of the pool, expressed as a sqrtPriceX96| **Returns** |Name|Type|Description| |----|----|-----------| |``|`int24`|The current tick of the pool, or type(int24).max if the pool creation failed, or the pool already existed| --- ## IPositionDescriptor [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IPositionDescriptor.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the PositionDescriptor contract ## Functions ### tokenURI Produces the URI describing a particular token ID *Note this URI may be a data: URI with the JSON contents directly inlined* ```solidity function tokenURI(IPositionManager positionManager, uint256 tokenId) external view returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`positionManager`|`IPositionManager`|The position manager for which to describe the token| |`tokenId`|`uint256`|The ID of the token for which to produce a description, which may not be valid| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The URI of the ERC721-compliant metadata| ### flipRatio Returns true if currency0 has higher priority than currency1 ```solidity function flipRatio(address currency0, address currency1) external view returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency0`|`address`|The first currency address| |`currency1`|`address`|The second currency address| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|True if currency0 has higher priority than currency1| ### currencyRatioPriority Returns the priority of a currency. For certain currencies on mainnet, the smaller the currency, the higher the priority And those with the higher priority values (more positive values) will be in the numerator of the price ratio ```solidity function currencyRatioPriority(address currency) external view returns (int256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`address`|The currency address| **Returns** |Name|Type|Description| |----|----|-----------| |``|`int256`|The priority of the currency| ### wrappedNative ```solidity function wrappedNative() external view returns (address); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`address`|The wrapped native token for this descriptor| ### nativeCurrencyLabel ```solidity function nativeCurrencyLabel() external view returns (string memory); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The native currency label for this descriptor| ### poolManager ```solidity function poolManager() external view returns (IPoolManager); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`IPoolManager`|The pool manager for this descriptor| ## Errors ### InvalidTokenId ```solidity error InvalidTokenId(uint256 tokenId); ``` --- ## IPositionManager [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IPositionManager.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [INotifier](contracts/v4/reference/periphery/interfaces/INotifier.md), [IImmutableState](contracts/v4/reference/periphery/interfaces/IImmutableState.md), [IERC721Permit_v4](contracts/v4/reference/periphery/interfaces/IERC721Permit_v4.md), [IEIP712_v4](contracts/v4/reference/periphery/interfaces/IEIP712_v4.md), [IMulticall_v4](contracts/v4/reference/periphery/interfaces/IMulticall_v4.md), [IPoolInitializer_v4](contracts/v4/reference/periphery/interfaces/IPoolInitializer_v4.md), [IUnorderedNonce](contracts/v4/reference/periphery/interfaces/IUnorderedNonce.md), [IPermit2Forwarder](contracts/v4/reference/periphery/interfaces/IPermit2Forwarder.md) Interface for the PositionManager contract ## Functions ### modifyLiquidities Unlocks Uniswap v4 PoolManager and batches actions for modifying liquidity *This is the standard entrypoint for the PositionManager* ```solidity function modifyLiquidities(bytes calldata unlockData, uint256 deadline) external payable; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`unlockData`|`bytes`|is an encoding of actions, and parameters for those actions| |`deadline`|`uint256`|is the deadline for the batched actions to be executed| ### modifyLiquiditiesWithoutUnlock Batches actions for modifying liquidity without unlocking v4 PoolManager *This must be called by a contract that has already unlocked the v4 PoolManager* ```solidity function modifyLiquiditiesWithoutUnlock(bytes calldata actions, bytes[] calldata params) external payable; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`actions`|`bytes`|the actions to perform| |`params`|`bytes[]`|the parameters to provide for the actions| ### nextTokenId Used to get the ID that will be used for the next minted liquidity position ```solidity function nextTokenId() external view returns (uint256); ``` **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|uint256 The next token ID| ### getPositionLiquidity Returns the liquidity of a position *this value can be processed as an amount0 and amount1 by using the LiquidityAmounts library* ```solidity function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|the position's liquidity, as a liquidityAmount| ### getPoolAndPositionInfo Returns the pool key and position info of a position ```solidity function getPoolAndPositionInfo(uint256 tokenId) external view returns (PoolKey memory, PositionInfo); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| **Returns** |Name|Type|Description| |----|----|-----------| |``|`PoolKey`|poolKey the pool key of the position| |``|`PositionInfo`|PositionInfo a uint256 packed value holding information about the position including the range (tickLower, tickUpper)| ### positionInfo Returns the position info of a position ```solidity function positionInfo(uint256 tokenId) external view returns (PositionInfo); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the ERC721 tokenId| **Returns** |Name|Type|Description| |----|----|-----------| |``|`PositionInfo`|a uint256 packed value holding information about the position including the range (tickLower, tickUpper)| ## Errors ### NotApproved Thrown when the caller is not approved to modify a position ```solidity error NotApproved(address caller); ``` ### DeadlinePassed Thrown when the block.timestamp exceeds the user-provided deadline ```solidity error DeadlinePassed(uint256 deadline); ``` ### PoolManagerMustBeLocked Thrown when calling transfer, subscribe, or unsubscribe when the PoolManager is unlocked. *This is to prevent hooks from being able to trigger notifications at the same time the position is being modified.* ```solidity error PoolManagerMustBeLocked(); ``` --- ## IStateView [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IStateView.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IImmutableState](contracts/v4/reference/periphery/interfaces/IImmutableState.md) Interface for the StateView contract ## Functions ### getSlot0 Get Slot0 of the pool: sqrtPriceX96, tick, protocolFee, lpFee *Corresponds to pools[poolId].slot0* ```solidity function getSlot0(PoolId poolId) external view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`sqrtPriceX96`|`uint160`|The square root of the price of the pool, in Q96 precision.| |`tick`|`int24`|The current tick of the pool.| |`protocolFee`|`uint24`|The protocol fee of the pool.| |`lpFee`|`uint24`|The swap fee of the pool.| ### getTickInfo Retrieves the tick information of a pool at a specific tick. *Corresponds to pools[poolId].ticks[tick]* ```solidity function getTickInfo(PoolId poolId, int24 tick) external view returns (uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int24`|The tick to retrieve information for.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidityGross`|`uint128`|The total position liquidity that references this tick| |`liquidityNet`|`int128`|The amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)| |`feeGrowthOutside0X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| |`feeGrowthOutside1X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| ### getTickLiquidity Retrieves the liquidity information of a pool at a specific tick. *Corresponds to pools[poolId].ticks[tick].liquidityGross and pools[poolId].ticks[tick].liquidityNet. A more gas efficient version of getTickInfo* ```solidity function getTickLiquidity(PoolId poolId, int24 tick) external view returns (uint128 liquidityGross, int128 liquidityNet); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int24`|The tick to retrieve liquidity for.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidityGross`|`uint128`|The total position liquidity that references this tick| |`liquidityNet`|`int128`|The amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)| ### getTickFeeGrowthOutside Retrieves the fee growth outside a tick range of a pool *Corresponds to pools[poolId].ticks[tick].feeGrowthOutside0X128 and pools[poolId].ticks[tick].feeGrowthOutside1X128. A more gas efficient version of getTickInfo* ```solidity function getTickFeeGrowthOutside(PoolId poolId, int24 tick) external view returns (uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int24`|The tick to retrieve fee growth for.| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthOutside0X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| |`feeGrowthOutside1X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| ### getFeeGrowthGlobals Retrieves the global fee growth of a pool. *Corresponds to pools[poolId].feeGrowthGlobal0X128 and pools[poolId].feeGrowthGlobal1X128* ```solidity function getFeeGrowthGlobals(PoolId poolId) external view returns (uint256 feeGrowthGlobal0, uint256 feeGrowthGlobal1); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthGlobal0`|`uint256`|The global fee growth for token0.| |`feeGrowthGlobal1`|`uint256`|The global fee growth for token1.| ### getLiquidity Retrieves the total liquidity of a pool. *Corresponds to pools[poolId].liquidity* ```solidity function getLiquidity(PoolId poolId) external view returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the pool.| ### getTickBitmap Retrieves the tick bitmap of a pool at a specific tick. *Corresponds to pools[poolId].tickBitmap[tick]* ```solidity function getTickBitmap(PoolId poolId, int16 tick) external view returns (uint256 tickBitmap); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int16`|The tick to retrieve the bitmap for.| **Returns** |Name|Type|Description| |----|----|-----------| |`tickBitmap`|`uint256`|The bitmap of the tick.| ### getPositionInfo Retrieves the position info without needing to calculate the `positionId`. *Corresponds to pools[poolId].positions[positionId]* ```solidity function getPositionInfo(PoolId poolId, address owner, int24 tickLower, int24 tickUpper, bytes32 salt) external view returns (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`owner`|`address`|The owner of the liquidity position.| |`tickLower`|`int24`|The lower tick of the liquidity range.| |`tickUpper`|`int24`|The upper tick of the liquidity range.| |`salt`|`bytes32`|The bytes32 randomness to further distinguish position state.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the position.| |`feeGrowthInside0LastX128`|`uint256`|The fee growth inside the position for token0.| |`feeGrowthInside1LastX128`|`uint256`|The fee growth inside the position for token1.| ### getPositionInfo Retrieves the position information of a pool at a specific position ID. *Corresponds to pools[poolId].positions[positionId]* ```solidity function getPositionInfo(PoolId poolId, bytes32 positionId) external view returns (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`positionId`|`bytes32`|The ID of the position.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the position.| |`feeGrowthInside0LastX128`|`uint256`|The fee growth inside the position for token0.| |`feeGrowthInside1LastX128`|`uint256`|The fee growth inside the position for token1.| ### getPositionLiquidity Retrieves the liquidity of a position. *Corresponds to pools[poolId].positions[positionId].liquidity. More gas efficient for just retrieving liquidity as compared to getPositionInfo* ```solidity function getPositionLiquidity(PoolId poolId, bytes32 positionId) external view returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`positionId`|`bytes32`|The ID of the position.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the position.| ### getFeeGrowthInside Calculate the fee growth inside a tick range of a pool *pools[poolId].feeGrowthInside0LastX128 in Position.Info is cached and can become stale. This function will calculate the up to date feeGrowthInside* ```solidity function getFeeGrowthInside(PoolId poolId, int24 tickLower, int24 tickUpper) external view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tickLower`|`int24`|The lower tick of the range.| |`tickUpper`|`int24`|The upper tick of the range.| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthInside0X128`|`uint256`|The fee growth inside the tick range for token0.| |`feeGrowthInside1X128`|`uint256`|The fee growth inside the tick range for token1.| --- ## ISubscriber [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/ISubscriber.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface that a Subscriber contract should implement to receive updates from the v4 position manager ## Functions ### notifySubscribe Called when a position subscribes to this subscriber contract ```solidity function notifySubscribe(uint256 tokenId, bytes memory data) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the token ID of the position| |`data`|`bytes`|additional data passed in by the caller| ### notifyUnsubscribe Called when a position unsubscribes from the subscriber *This call's gas is capped at `unsubscribeGasLimit` (set at deployment)* *Because of EIP-150, solidity may only allocate 63/64 of gasleft()* ```solidity function notifyUnsubscribe(uint256 tokenId) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the token ID of the position| ### notifyBurn Called when a position is burned ```solidity function notifyBurn(uint256 tokenId, address owner, PositionInfo info, uint256 liquidity, BalanceDelta feesAccrued) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the token ID of the position| |`owner`|`address`|the current owner of the tokenId| |`info`|`PositionInfo`|information about the position| |`liquidity`|`uint256`|the amount of liquidity decreased in the position, may be 0| |`feesAccrued`|`BalanceDelta`|the fees accrued by the position if liquidity was decreased| ### notifyModifyLiquidity Called when a position modifies its liquidity or collects fees *Note that feesAccrued can be artificially inflated by a malicious user Pools with a single liquidity position can inflate feeGrowthGlobal (and consequently feesAccrued) by donating to themselves; atomically donating and collecting fees within the same unlockCallback may further inflate feeGrowthGlobal/feesAccrued* ```solidity function notifyModifyLiquidity(uint256 tokenId, int256 liquidityChange, BalanceDelta feesAccrued) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|the token ID of the position| |`liquidityChange`|`int256`|the change in liquidity on the underlying position| |`feesAccrued`|`BalanceDelta`|the fees to be collected from the position as a result of the modifyLiquidity call| --- ## IUniswapV4DeployerCompetition [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IUniswapV4DeployerCompetition.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the UniswapV4DeployerCompetition contract ## Functions ### updateBestAddress Updates the best address if the new address has a better vanity score *The first 20 bytes of the salt must be either address(0) or msg.sender* ```solidity function updateBestAddress(bytes32 salt) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`salt`|`bytes32`|The salt to use to compute the new address with CREATE2| ### deploy deploys the Uniswap v4 PoolManager contract *The bytecode must match the initCodeHash* ```solidity function deploy(bytes memory bytecode) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`bytecode`|`bytes`|The bytecode of the Uniswap v4 PoolManager contract| ## Events ### NewAddressFound ```solidity event NewAddressFound(address indexed bestAddress, address indexed submitter, uint256 score); ``` ## Errors ### InvalidBytecode ```solidity error InvalidBytecode(); ``` ### CompetitionNotOver ```solidity error CompetitionNotOver(uint256 currentTime, uint256 deadline); ``` ### CompetitionOver ```solidity error CompetitionOver(uint256 currentTime, uint256 deadline); ``` ### NotAllowedToDeploy ```solidity error NotAllowedToDeploy(address sender, address deployer); ``` ### WorseAddress ```solidity error WorseAddress(address newAddress, address bestAddress, uint256 newScore, uint256 bestScore); ``` ### InvalidSender ```solidity error InvalidSender(bytes32 salt, address sender); ``` --- ## IUnorderedNonce [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IUnorderedNonce.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Interface for the UnorderedNonce contract ## Functions ### nonces mapping of nonces consumed by each address, where a nonce is a single bit on the 256-bit bitmap *word is at most type(uint248).max* ```solidity function nonces(address owner, uint256 word) external view returns (uint256); ``` ### revokeNonce Revoke a nonce by spending it, preventing it from being used again *Used in cases where a valid nonce has not been broadcasted onchain, and the owner wants to revoke the validity of the nonce* *payable so it can be multicalled with native-token related actions* ```solidity function revokeNonce(uint256 nonce) external payable; ``` ## Errors ### NonceAlreadyUsed ```solidity error NonceAlreadyUsed(); ``` --- ## IV4Quoter [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IV4Quoter.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IImmutableState](contracts/v4/reference/periphery/interfaces/IImmutableState.md) Interface for the V4Quoter contract ## Functions ### quoteExactInputSingle Returns the delta amounts for a given exact input swap of a single pool ```solidity function quoteExactInputSingle(QuoteExactSingleParams memory params) external returns (uint256 amountOut, uint256 gasEstimate); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`QuoteExactSingleParams`|The params for the quote, encoded as `QuoteExactSingleParams` poolKey The key for identifying a V4 pool zeroForOne If the swap is from currency0 to currency1 exactAmount The desired input amount hookData arbitrary hookData to pass into the associated hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`amountOut`|`uint256`|The output quote for the exactIn swap| |`gasEstimate`|`uint256`|Estimated gas units used for the swap| ### quoteExactInput Returns the delta amounts along the swap path for a given exact input swap ```solidity function quoteExactInput(QuoteExactParams memory params) external returns (uint256 amountOut, uint256 gasEstimate); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`QuoteExactParams`|the params for the quote, encoded as 'QuoteExactParams' currencyIn The input currency of the swap path The path of the swap encoded as PathKeys that contains currency, fee, tickSpacing, and hook info exactAmount The desired input amount| **Returns** |Name|Type|Description| |----|----|-----------| |`amountOut`|`uint256`|The output quote for the exactIn swap| |`gasEstimate`|`uint256`|Estimated gas units used for the swap| ### quoteExactOutputSingle Returns the delta amounts for a given exact output swap of a single pool ```solidity function quoteExactOutputSingle(QuoteExactSingleParams memory params) external returns (uint256 amountIn, uint256 gasEstimate); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`QuoteExactSingleParams`|The params for the quote, encoded as `QuoteExactSingleParams` poolKey The key for identifying a V4 pool zeroForOne If the swap is from currency0 to currency1 exactAmount The desired output amount hookData arbitrary hookData to pass into the associated hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`amountIn`|`uint256`|The input quote for the exactOut swap| |`gasEstimate`|`uint256`|Estimated gas units used for the swap| ### quoteExactOutput Returns the delta amounts along the swap path for a given exact output swap ```solidity function quoteExactOutput(QuoteExactParams memory params) external returns (uint256 amountIn, uint256 gasEstimate); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`QuoteExactParams`|the params for the quote, encoded as 'QuoteExactParams' currencyOut The output currency of the swap path The path of the swap encoded as PathKeys that contains currency, fee, tickSpacing, and hook info exactAmount The desired output amount| **Returns** |Name|Type|Description| |----|----|-----------| |`amountIn`|`uint256`|The input quote for the exactOut swap| |`gasEstimate`|`uint256`|Estimated gas units used for the swap| ## Structs ### QuoteExactSingleParams ```solidity struct QuoteExactSingleParams { PoolKey poolKey; bool zeroForOne; uint128 exactAmount; bytes hookData; } ``` ### QuoteExactParams ```solidity struct QuoteExactParams { Currency exactCurrency; PathKey[] path; uint128 exactAmount; } ``` --- ## IV4Router [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/IV4Router.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IImmutableState](contracts/v4/reference/periphery/interfaces/IImmutableState.md) Interface for the V4Router contract ## Errors ### V4TooLittleReceived Emitted when an exactInput swap does not receive its minAmountOut ```solidity error V4TooLittleReceived(uint256 minAmountOutReceived, uint256 amountReceived); ``` ### V4TooMuchRequested Emitted when an exactOutput is asked for more than its maxAmountIn ```solidity error V4TooMuchRequested(uint256 maxAmountInRequested, uint256 amountRequested); ``` ## Structs ### ExactInputSingleParams Parameters for a single-hop exact-input swap ```solidity struct ExactInputSingleParams { PoolKey poolKey; bool zeroForOne; uint128 amountIn; uint128 amountOutMinimum; bytes hookData; } ``` ### ExactInputParams Parameters for a multi-hop exact-input swap ```solidity struct ExactInputParams { Currency currencyIn; PathKey[] path; uint128 amountIn; uint128 amountOutMinimum; } ``` ### ExactOutputSingleParams Parameters for a single-hop exact-output swap ```solidity struct ExactOutputSingleParams { PoolKey poolKey; bool zeroForOne; uint128 amountOut; uint128 amountInMaximum; bytes hookData; } ``` ### ExactOutputParams Parameters for a multi-hop exact-output swap ```solidity struct ExactOutputParams { Currency currencyOut; PathKey[] path; uint128 amountOut; uint128 amountInMaximum; } ``` --- ## IWETH9 [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/interfaces/external/IWETH9.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** IERC20 ## Functions ### deposit Deposit ether to get wrapped ether ```solidity function deposit() external payable; ``` ### withdraw Withdraw wrapped ether to get ether ```solidity function withdraw(uint256) external; ``` --- ## StateView(Lens) [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/lens/StateView.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [ImmutableState](contracts/v4/reference/periphery/base/ImmutableState.md), [IStateView](contracts/v4/reference/periphery/interfaces/IStateView.md) A view only contract wrapping the StateLibrary.sol library for reading storage in v4-core. *The contract is intended for offchain clients. Use StateLibrary.sol directly if reading state onchain.* ## Functions ### constructor ```solidity constructor(IPoolManager _poolManager) ImmutableState(_poolManager); ``` ### getSlot0 Get Slot0 of the pool: sqrtPriceX96, tick, protocolFee, lpFee *Corresponds to pools[poolId].slot0* ```solidity function getSlot0(PoolId poolId) external view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`sqrtPriceX96`|`uint160`|The square root of the price of the pool, in Q96 precision.| |`tick`|`int24`|The current tick of the pool.| |`protocolFee`|`uint24`|The protocol fee of the pool.| |`lpFee`|`uint24`|The swap fee of the pool.| ### getTickInfo Retrieves the tick information of a pool at a specific tick. *Corresponds to pools[poolId].ticks[tick]* ```solidity function getTickInfo(PoolId poolId, int24 tick) external view returns (uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int24`|The tick to retrieve information for.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidityGross`|`uint128`|The total position liquidity that references this tick| |`liquidityNet`|`int128`|The amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)| |`feeGrowthOutside0X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| |`feeGrowthOutside1X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| ### getTickLiquidity Retrieves the liquidity information of a pool at a specific tick. *Corresponds to pools[poolId].ticks[tick].liquidityGross and pools[poolId].ticks[tick].liquidityNet. A more gas efficient version of getTickInfo* ```solidity function getTickLiquidity(PoolId poolId, int24 tick) external view returns (uint128 liquidityGross, int128 liquidityNet); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int24`|The tick to retrieve liquidity for.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidityGross`|`uint128`|The total position liquidity that references this tick| |`liquidityNet`|`int128`|The amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)| ### getTickFeeGrowthOutside Retrieves the fee growth outside a tick range of a pool *Corresponds to pools[poolId].ticks[tick].feeGrowthOutside0X128 and pools[poolId].ticks[tick].feeGrowthOutside1X128. A more gas efficient version of getTickInfo* ```solidity function getTickFeeGrowthOutside(PoolId poolId, int24 tick) external view returns (uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int24`|The tick to retrieve fee growth for.| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthOutside0X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| |`feeGrowthOutside1X128`|`uint256`|fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)| ### getFeeGrowthGlobals Retrieves the global fee growth of a pool. *Corresponds to pools[poolId].feeGrowthGlobal0X128 and pools[poolId].feeGrowthGlobal1X128* ```solidity function getFeeGrowthGlobals(PoolId poolId) external view returns (uint256 feeGrowthGlobal0, uint256 feeGrowthGlobal1); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthGlobal0`|`uint256`|The global fee growth for token0.| |`feeGrowthGlobal1`|`uint256`|The global fee growth for token1.| ### getLiquidity Retrieves the total liquidity of a pool. *Corresponds to pools[poolId].liquidity* ```solidity function getLiquidity(PoolId poolId) external view returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the pool.| ### getTickBitmap Retrieves the tick bitmap of a pool at a specific tick. *Corresponds to pools[poolId].tickBitmap[tick]* ```solidity function getTickBitmap(PoolId poolId, int16 tick) external view returns (uint256 tickBitmap); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tick`|`int16`|The tick to retrieve the bitmap for.| **Returns** |Name|Type|Description| |----|----|-----------| |`tickBitmap`|`uint256`|The bitmap of the tick.| ### getPositionInfo Retrieves the position info without needing to calculate the `positionId`. *Corresponds to pools[poolId].positions[positionId]* ```solidity function getPositionInfo(PoolId poolId, address owner, int24 tickLower, int24 tickUpper, bytes32 salt) external view returns (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`owner`|`address`|The owner of the liquidity position.| |`tickLower`|`int24`|The lower tick of the liquidity range.| |`tickUpper`|`int24`|The upper tick of the liquidity range.| |`salt`|`bytes32`|The bytes32 randomness to further distinguish position state.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the position.| |`feeGrowthInside0LastX128`|`uint256`|The fee growth inside the position for token0.| |`feeGrowthInside1LastX128`|`uint256`|The fee growth inside the position for token1.| ### getPositionInfo Retrieves the position info without needing to calculate the `positionId`. *Corresponds to pools[poolId].positions[positionId]* ```solidity function getPositionInfo(PoolId poolId, bytes32 positionId) external view returns (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`positionId`|`bytes32`|| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the position.| |`feeGrowthInside0LastX128`|`uint256`|The fee growth inside the position for token0.| |`feeGrowthInside1LastX128`|`uint256`|The fee growth inside the position for token1.| ### getPositionLiquidity Retrieves the liquidity of a position. *Corresponds to pools[poolId].positions[positionId].liquidity. More gas efficient for just retrieving liquidity as compared to getPositionInfo* ```solidity function getPositionLiquidity(PoolId poolId, bytes32 positionId) external view returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`positionId`|`bytes32`|The ID of the position.| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The liquidity of the position.| ### getFeeGrowthInside Calculate the fee growth inside a tick range of a pool *pools[poolId].feeGrowthInside0LastX128 in Position.Info is cached and can become stale. This function will calculate the up to date feeGrowthInside* ```solidity function getFeeGrowthInside(PoolId poolId, int24 tickLower, int24 tickUpper) external view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`poolId`|`PoolId`|The ID of the pool.| |`tickLower`|`int24`|The lower tick of the range.| |`tickUpper`|`int24`|The upper tick of the range.| **Returns** |Name|Type|Description| |----|----|-----------| |`feeGrowthInside0X128`|`uint256`|The fee growth inside the tick range for token0.| |`feeGrowthInside1X128`|`uint256`|The fee growth inside the tick range for token1.| --- ## V4Quoter [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/lens/V4Quoter.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** [IV4Quoter](contracts/v4/reference/periphery/interfaces/IV4Quoter.md), [BaseV4Quoter](contracts/v4/reference/periphery/base/BaseV4Quoter.md) Supports quoting the delta amounts for exact input or exact output swaps. *These functions are not marked view because they rely on calling non-view functions and reverting to compute the result. They are also not gas efficient and should not be called on-chain.* ## Functions ### constructor ```solidity constructor(IPoolManager _poolManager) BaseV4Quoter(_poolManager); ``` ### quoteExactInputSingle Returns the delta amounts for a given exact input swap of a single pool ```solidity function quoteExactInputSingle(QuoteExactSingleParams memory params) external returns (uint256 amountOut, uint256 gasEstimate); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`QuoteExactSingleParams`|The params for the quote, encoded as `QuoteExactSingleParams` poolKey The key for identifying a V4 pool zeroForOne If the swap is from currency0 to currency1 exactAmount The desired input amount hookData arbitrary hookData to pass into the associated hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`amountOut`|`uint256`|The output quote for the exactIn swap| |`gasEstimate`|`uint256`|Estimated gas units used for the swap| ### quoteExactInput Returns the delta amounts along the swap path for a given exact input swap ```solidity function quoteExactInput(QuoteExactParams memory params) external returns (uint256 amountOut, uint256 gasEstimate); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`QuoteExactParams`|the params for the quote, encoded as 'QuoteExactParams' currencyIn The input currency of the swap path The path of the swap encoded as PathKeys that contains currency, fee, tickSpacing, and hook info exactAmount The desired input amount| **Returns** |Name|Type|Description| |----|----|-----------| |`amountOut`|`uint256`|The output quote for the exactIn swap| |`gasEstimate`|`uint256`|Estimated gas units used for the swap| ### quoteExactOutputSingle Returns the delta amounts for a given exact output swap of a single pool ```solidity function quoteExactOutputSingle(QuoteExactSingleParams memory params) external returns (uint256 amountIn, uint256 gasEstimate); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`QuoteExactSingleParams`|The params for the quote, encoded as `QuoteExactSingleParams` poolKey The key for identifying a V4 pool zeroForOne If the swap is from currency0 to currency1 exactAmount The desired output amount hookData arbitrary hookData to pass into the associated hooks| **Returns** |Name|Type|Description| |----|----|-----------| |`amountIn`|`uint256`|The input quote for the exactOut swap| |`gasEstimate`|`uint256`|Estimated gas units used for the swap| ### quoteExactOutput Returns the delta amounts along the swap path for a given exact output swap ```solidity function quoteExactOutput(QuoteExactParams memory params) external returns (uint256 amountIn, uint256 gasEstimate); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`QuoteExactParams`|the params for the quote, encoded as 'QuoteExactParams' currencyOut The output currency of the swap path The path of the swap encoded as PathKeys that contains currency, fee, tickSpacing, and hook info exactAmount The desired output amount| **Returns** |Name|Type|Description| |----|----|-----------| |`amountIn`|`uint256`|The input quote for the exactOut swap| |`gasEstimate`|`uint256`|Estimated gas units used for the swap| ### _quoteExactInput *external function called within the _unlockCallback, to simulate an exact input swap, then revert with the result* ```solidity function _quoteExactInput(QuoteExactParams calldata params) external selfOnly returns (bytes memory); ``` ### _quoteExactInputSingle *external function called within the _unlockCallback, to simulate a single-hop exact input swap, then revert with the result* ```solidity function _quoteExactInputSingle(QuoteExactSingleParams calldata params) external selfOnly returns (bytes memory); ``` ### _quoteExactOutput *external function called within the _unlockCallback, to simulate an exact output swap, then revert with the result* ```solidity function _quoteExactOutput(QuoteExactParams calldata params) external selfOnly returns (bytes memory); ``` ### _quoteExactOutputSingle *external function called within the _unlockCallback, to simulate a single-hop exact output swap, then revert with the result* ```solidity function _quoteExactOutputSingle(QuoteExactSingleParams calldata params) external selfOnly returns (bytes memory); ``` --- ## ActionConstants [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/ActionConstants.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Common constants used in actions *Constants are gas efficient alternatives to their literal values* ## State Variables ### OPEN_DELTA used to signal that an action should use the input value of the open delta on the pool manager or of the balance that the contract holds ```solidity uint128 internal constant OPEN_DELTA = 0; ``` ### CONTRACT_BALANCE used to signal that an action should use the contract's entire balance of a currency This value is equivalent to 1\<\<255, i.e. a singular 1 in the most significant bit. ```solidity uint256 internal constant CONTRACT_BALANCE = 0x8000000000000000000000000000000000000000000000000000000000000000; ``` ### MSG_SENDER used to signal that the recipient of an action should be the msgSender ```solidity address internal constant MSG_SENDER = address(1); ``` ### ADDRESS_THIS used to signal that the recipient of an action should be the address(this) ```solidity address internal constant ADDRESS_THIS = address(2); ``` --- ## Actions [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/Actions.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Library to define different pool actions. *These are suggested common commands, however additional commands should be defined as required Some of these actions are not supported in the Router contracts or Position Manager contracts, but are left as they may be helpful commands for other peripheral contracts.* ## State Variables ### INCREASE_LIQUIDITY ```solidity uint256 internal constant INCREASE_LIQUIDITY = 0x00; ``` ### DECREASE_LIQUIDITY ```solidity uint256 internal constant DECREASE_LIQUIDITY = 0x01; ``` ### MINT_POSITION ```solidity uint256 internal constant MINT_POSITION = 0x02; ``` ### BURN_POSITION ```solidity uint256 internal constant BURN_POSITION = 0x03; ``` ### INCREASE_LIQUIDITY_FROM_DELTAS ```solidity uint256 internal constant INCREASE_LIQUIDITY_FROM_DELTAS = 0x04; ``` ### MINT_POSITION_FROM_DELTAS ```solidity uint256 internal constant MINT_POSITION_FROM_DELTAS = 0x05; ``` ### SWAP_EXACT_IN_SINGLE ```solidity uint256 internal constant SWAP_EXACT_IN_SINGLE = 0x06; ``` ### SWAP_EXACT_IN ```solidity uint256 internal constant SWAP_EXACT_IN = 0x07; ``` ### SWAP_EXACT_OUT_SINGLE ```solidity uint256 internal constant SWAP_EXACT_OUT_SINGLE = 0x08; ``` ### SWAP_EXACT_OUT ```solidity uint256 internal constant SWAP_EXACT_OUT = 0x09; ``` ### DONATE ```solidity uint256 internal constant DONATE = 0x0a; ``` ### SETTLE ```solidity uint256 internal constant SETTLE = 0x0b; ``` ### SETTLE_ALL ```solidity uint256 internal constant SETTLE_ALL = 0x0c; ``` ### SETTLE_PAIR ```solidity uint256 internal constant SETTLE_PAIR = 0x0d; ``` ### TAKE ```solidity uint256 internal constant TAKE = 0x0e; ``` ### TAKE_ALL ```solidity uint256 internal constant TAKE_ALL = 0x0f; ``` ### TAKE_PORTION ```solidity uint256 internal constant TAKE_PORTION = 0x10; ``` ### TAKE_PAIR ```solidity uint256 internal constant TAKE_PAIR = 0x11; ``` ### CLOSE_CURRENCY ```solidity uint256 internal constant CLOSE_CURRENCY = 0x12; ``` ### CLEAR_OR_TAKE ```solidity uint256 internal constant CLEAR_OR_TAKE = 0x13; ``` ### SWEEP ```solidity uint256 internal constant SWEEP = 0x14; ``` ### WRAP ```solidity uint256 internal constant WRAP = 0x15; ``` ### UNWRAP ```solidity uint256 internal constant UNWRAP = 0x16; ``` ### MINT_6909 ```solidity uint256 internal constant MINT_6909 = 0x17; ``` ### BURN_6909 ```solidity uint256 internal constant BURN_6909 = 0x18; ``` --- ## AddressStringUtil [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/AddressStringUtil.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) provides utility functions for converting addresses to strings *Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/AddressStringUtil.sol* ## Functions ### toAsciiString Converts an address to the uppercase hex string, extracting only len bytes (up to 20, multiple of 2) ```solidity function toAsciiString(address addr, uint256 len) internal pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`addr`|`address`|the address to convert| |`len`|`uint256`|the number of bytes to extract| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|the hex string| ### char Converts a value into is corresponding ASCII character for the hex representation ```solidity function char(uint8 b) private pure returns (bytes1 c); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`b`|`uint8`|the value to convert| **Returns** |Name|Type|Description| |----|----|-----------| |`c`|`bytes1`|the ASCII character| ## Errors ### InvalidAddressLength ```solidity error InvalidAddressLength(uint256 len); ``` --- ## BipsLibrary [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/BipsLibrary.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## State Variables ### BPS_DENOMINATOR ```solidity uint256 internal constant BPS_DENOMINATOR = 10_000; ``` ## Functions ### calculatePortion ```solidity function calculatePortion(uint256 amount, uint256 bips) internal pure returns (uint256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`amount`|`uint256`|The total amount to calculate a percentage of| |`bips`|`uint256`|The percentage to calculate, in bips| ## Errors ### InvalidBips emitted when an invalid percentage is provided ```solidity error InvalidBips(); ``` --- ## CalldataDecoder [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/CalldataDecoder.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## State Variables ### OFFSET_OR_LENGTH_MASK mask used for offsets and lengths to ensure no overflow *no sane abi encoding will pass in an offset or length greater than type(uint32).max (note that this does deviate from standard solidity behavior and offsets/lengths will be interpreted as mod type(uint32).max which will only impact malicious/buggy callers)* ```solidity uint256 constant OFFSET_OR_LENGTH_MASK = 0xffffffff; ``` ### OFFSET_OR_LENGTH_MASK_AND_WORD_ALIGN ```solidity uint256 constant OFFSET_OR_LENGTH_MASK_AND_WORD_ALIGN = 0xffffffe0; ``` ### SLICE_ERROR_SELECTOR equivalent to SliceOutOfBounds.selector, stored in least-significant bits ```solidity uint256 constant SLICE_ERROR_SELECTOR = 0x3b99b53d; ``` ## Functions ### decodeActionsRouterParams *equivalent to: abi.decode(params, (bytes, bytes[])) in calldata (requires strict abi encoding)* ```solidity function decodeActionsRouterParams(bytes calldata _bytes) internal pure returns (bytes calldata actions, bytes[] calldata params); ``` ### decodeModifyLiquidityParams *equivalent to: abi.decode(params, (uint256, uint256, uint128, uint128, bytes)) in calldata* ```solidity function decodeModifyLiquidityParams(bytes calldata params) internal pure returns (uint256 tokenId, uint256 liquidity, uint128 amount0, uint128 amount1, bytes calldata hookData); ``` ### decodeIncreaseLiquidityFromDeltasParams *equivalent to: abi.decode(params, (uint256, uint128, uint128, bytes)) in calldata* ```solidity function decodeIncreaseLiquidityFromDeltasParams(bytes calldata params) internal pure returns (uint256 tokenId, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData); ``` ### decodeMintParams *equivalent to: abi.decode(params, (PoolKey, int24, int24, uint256, uint128, uint128, address, bytes)) in calldata* ```solidity function decodeMintParams(bytes calldata params) internal pure returns ( PoolKey calldata poolKey, int24 tickLower, int24 tickUpper, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, address owner, bytes calldata hookData ); ``` ### decodeMintFromDeltasParams *equivalent to: abi.decode(params, (PoolKey, int24, int24, uint128, uint128, address, bytes)) in calldata* ```solidity function decodeMintFromDeltasParams(bytes calldata params) internal pure returns ( PoolKey calldata poolKey, int24 tickLower, int24 tickUpper, uint128 amount0Max, uint128 amount1Max, address owner, bytes calldata hookData ); ``` ### decodeBurnParams *equivalent to: abi.decode(params, (uint256, uint128, uint128, bytes)) in calldata* ```solidity function decodeBurnParams(bytes calldata params) internal pure returns (uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData); ``` ### decodeSwapExactInParams *equivalent to: abi.decode(params, (IV4Router.ExactInputParams))* ```solidity function decodeSwapExactInParams(bytes calldata params) internal pure returns (IV4Router.ExactInputParams calldata swapParams); ``` ### decodeSwapExactInSingleParams *equivalent to: abi.decode(params, (IV4Router.ExactInputSingleParams))* ```solidity function decodeSwapExactInSingleParams(bytes calldata params) internal pure returns (IV4Router.ExactInputSingleParams calldata swapParams); ``` ### decodeSwapExactOutParams *equivalent to: abi.decode(params, (IV4Router.ExactOutputParams))* ```solidity function decodeSwapExactOutParams(bytes calldata params) internal pure returns (IV4Router.ExactOutputParams calldata swapParams); ``` ### decodeSwapExactOutSingleParams *equivalent to: abi.decode(params, (IV4Router.ExactOutputSingleParams))* ```solidity function decodeSwapExactOutSingleParams(bytes calldata params) internal pure returns (IV4Router.ExactOutputSingleParams calldata swapParams); ``` ### decodeCurrency *equivalent to: abi.decode(params, (Currency)) in calldata* ```solidity function decodeCurrency(bytes calldata params) internal pure returns (Currency currency); ``` ### decodeCurrencyPair *equivalent to: abi.decode(params, (Currency, Currency)) in calldata* ```solidity function decodeCurrencyPair(bytes calldata params) internal pure returns (Currency currency0, Currency currency1); ``` ### decodeCurrencyPairAndAddress *equivalent to: abi.decode(params, (Currency, Currency, address)) in calldata* ```solidity function decodeCurrencyPairAndAddress(bytes calldata params) internal pure returns (Currency currency0, Currency currency1, address _address); ``` ### decodeCurrencyAndAddress *equivalent to: abi.decode(params, (Currency, address)) in calldata* ```solidity function decodeCurrencyAndAddress(bytes calldata params) internal pure returns (Currency currency, address _address); ``` ### decodeCurrencyAddressAndUint256 *equivalent to: abi.decode(params, (Currency, address, uint256)) in calldata* ```solidity function decodeCurrencyAddressAndUint256(bytes calldata params) internal pure returns (Currency currency, address _address, uint256 amount); ``` ### decodeCurrencyAndUint256 *equivalent to: abi.decode(params, (Currency, uint256)) in calldata* ```solidity function decodeCurrencyAndUint256(bytes calldata params) internal pure returns (Currency currency, uint256 amount); ``` ### decodeUint256 *equivalent to: abi.decode(params, (uint256)) in calldata* ```solidity function decodeUint256(bytes calldata params) internal pure returns (uint256 amount); ``` ### decodeCurrencyUint256AndBool *equivalent to: abi.decode(params, (Currency, uint256, bool)) in calldata* ```solidity function decodeCurrencyUint256AndBool(bytes calldata params) internal pure returns (Currency currency, uint256 amount, bool boolean); ``` ### toBytes Decode the `_arg`-th element in `_bytes` as `bytes` ```solidity function toBytes(bytes calldata _bytes, uint256 _arg) internal pure returns (bytes calldata res); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_bytes`|`bytes`|The input bytes string to extract a bytes string from| |`_arg`|`uint256`|The index of the argument to extract| ## Errors ### SliceOutOfBounds ```solidity error SliceOutOfBounds(); ``` --- ## CurrencyRatioSortOrder [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/CurrencyRatioSortOrder.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Provides constants for sorting currencies when displaying price ratios Currencies given larger values will be in the numerator of the price ratio *Reference: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/TokenRatioSortOrder.sol* ## State Variables ### NUMERATOR_MOST ```solidity int256 constant NUMERATOR_MOST = 300; ``` ### NUMERATOR_MORE ```solidity int256 constant NUMERATOR_MORE = 200; ``` ### NUMERATOR ```solidity int256 constant NUMERATOR = 100; ``` ### DENOMINATOR_MOST ```solidity int256 constant DENOMINATOR_MOST = -300; ``` ### DENOMINATOR_MORE ```solidity int256 constant DENOMINATOR_MORE = -200; ``` ### DENOMINATOR ```solidity int256 constant DENOMINATOR = -100; ``` --- ## Descriptor [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/Descriptor.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Describes NFT token positions *Reference: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/NFTDescriptor.sol* ## State Variables ### sqrt10X128 ```solidity uint256 constant sqrt10X128 = 1076067327063303206878105757264492625226; ``` ## Functions ### constructTokenURI Constructs the token URI for a Uniswap v4 NFT ```solidity function constructTokenURI(ConstructTokenURIParams memory params) internal pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`ConstructTokenURIParams`|Parameters needed to construct the token URI| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The token URI as a string| ### escapeSpecialCharacters Escapes special characters in a string if they are present ```solidity function escapeSpecialCharacters(string memory symbol) internal pure returns (string memory); ``` ### generateDescriptionPartOne Generates the first part of the description for a Uniswap v4 NFT ```solidity function generateDescriptionPartOne( string memory quoteCurrencySymbol, string memory baseCurrencySymbol, string memory poolManager ) private pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`quoteCurrencySymbol`|`string`|The symbol of the quote currency| |`baseCurrencySymbol`|`string`|The symbol of the base currency| |`poolManager`|`string`|The address of the pool manager| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The first part of the description| ### generateDescriptionPartTwo Generates the second part of the description for a Uniswap v4 NFTs ```solidity function generateDescriptionPartTwo( string memory tokenId, string memory baseCurrencySymbol, string memory quoteCurrency, string memory baseCurrency, string memory hooks, string memory feeTier ) private pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`string`|The token ID| |`baseCurrencySymbol`|`string`|The symbol of the base currency| |`quoteCurrency`|`string`|The address of the quote currency| |`baseCurrency`|`string`|The address of the base currency| |`hooks`|`string`|The address of the hooks contract| |`feeTier`|`string`|The fee tier of the pool| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The second part of the description| ### generateName Generates the name for a Uniswap v4 NFT ```solidity function generateName(ConstructTokenURIParams memory params, string memory feeTier) private pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`ConstructTokenURIParams`|Parameters needed to generate the name| |`feeTier`|`string`|The fee tier of the pool| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The name of the NFT| ### generateDecimalString ```solidity function generateDecimalString(DecimalStringParams memory params) private pure returns (string memory); ``` ### tickToDecimalString Gets the price (quote/base) at a specific tick in decimal form MIN or MAX are returned if tick is at the bottom or top of the price curve ```solidity function tickToDecimalString( int24 tick, int24 tickSpacing, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals, bool flipRatio ) internal pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tick`|`int24`|The tick (either tickLower or tickUpper)| |`tickSpacing`|`int24`|The tick spacing of the pool| |`baseCurrencyDecimals`|`uint8`|The decimals of the base currency| |`quoteCurrencyDecimals`|`uint8`|The decimals of the quote currency| |`flipRatio`|`bool`|True if the ratio was flipped| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The ratio value as a string| ### sigfigsRounded ```solidity function sigfigsRounded(uint256 value, uint8 digits) private pure returns (uint256, bool); ``` ### adjustForDecimalPrecision Adjusts the sqrt price for different currencies with different decimals ```solidity function adjustForDecimalPrecision(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) private pure returns (uint256 adjustedSqrtRatioX96); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtRatioX96`|`uint160`|The sqrt price at a specific tick| |`baseCurrencyDecimals`|`uint8`|The decimals of the base currency| |`quoteCurrencyDecimals`|`uint8`|The decimals of the quote currency| **Returns** |Name|Type|Description| |----|----|-----------| |`adjustedSqrtRatioX96`|`uint256`|The adjusted sqrt price| ### abs Absolute value of a signed integer ```solidity function abs(int256 x) private pure returns (uint256); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`x`|`int256`|The signed integer| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint256`|The absolute value of x| ### fixedPointToDecimalString ```solidity function fixedPointToDecimalString(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) internal pure returns (string memory); ``` ### feeToPercentString Converts fee amount in pips to decimal string with percent sign ```solidity function feeToPercentString(uint24 fee) internal pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`fee`|`uint24`|fee amount| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|fee as a decimal string with percent sign| ### addressToString ```solidity function addressToString(address addr) internal pure returns (string memory); ``` ### generateSVGImage Generates the SVG image for a Uniswap v4 NFT ```solidity function generateSVGImage(ConstructTokenURIParams memory params) internal pure returns (string memory svg); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`ConstructTokenURIParams`|Parameters needed to generate the SVG image| **Returns** |Name|Type|Description| |----|----|-----------| |`svg`|`string`|The SVG image as a string| ### overRange Checks if the current price is within your position range, above, or below ```solidity function overRange(int24 tickLower, int24 tickUpper, int24 tickCurrent) private pure returns (int8); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tickLower`|`int24`|The lower tick| |`tickUpper`|`int24`|The upper tick| |`tickCurrent`|`int24`|The current tick| **Returns** |Name|Type|Description| |----|----|-----------| |``|`int8`|0 if the current price is within the position range, -1 if below, 1 if above| ### isSpecialCharacter ```solidity function isSpecialCharacter(bytes1 b) private pure returns (bool); ``` ### scale ```solidity function scale(uint256 n, uint256 inMn, uint256 inMx, uint256 outMn, uint256 outMx) private pure returns (string memory); ``` ### currencyToColorHex ```solidity function currencyToColorHex(uint256 currency, uint256 offset) internal pure returns (string memory str); ``` ### getCircleCoord ```solidity function getCircleCoord(uint256 currency, uint256 offset, uint256 tokenId) internal pure returns (uint256); ``` ### sliceCurrencyHex ```solidity function sliceCurrencyHex(uint256 currency, uint256 offset) internal pure returns (uint256); ``` ## Structs ### ConstructTokenURIParams ```solidity struct ConstructTokenURIParams { uint256 tokenId; address quoteCurrency; address baseCurrency; string quoteCurrencySymbol; string baseCurrencySymbol; uint8 quoteCurrencyDecimals; uint8 baseCurrencyDecimals; bool flipRatio; int24 tickLower; int24 tickUpper; int24 tickCurrent; int24 tickSpacing; uint24 fee; address poolManager; address hooks; } ``` ### DecimalStringParams ```solidity struct DecimalStringParams { uint256 sigfigs; uint8 bufferLength; uint8 sigfigIndex; uint8 decimalIndex; uint8 zerosStartIndex; uint8 zerosEndIndex; bool isLessThanOne; bool isPercent; } ``` --- ## ERC721PermitHash [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/ERC721PermitHash.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## State Variables ### PERMIT_TYPEHASH *Value is equal to keccak256("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)");* ```solidity bytes32 constant PERMIT_TYPEHASH = 0x49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad; ``` ### PERMIT_FOR_ALL_TYPEHASH *Value is equal to keccak256("PermitForAll(address operator,bool approved,uint256 nonce,uint256 deadline)");* ```solidity bytes32 constant PERMIT_FOR_ALL_TYPEHASH = 0x6673cb397ee2a50b6b8401653d3638b4ac8b3db9c28aa6870ffceb7574ec2f76; ``` ## Functions ### hashPermit Hashes the data that will be signed for IERC721Permit_v4.permit() ```solidity function hashPermit(address spender, uint256 tokenId, uint256 nonce, uint256 deadline) internal pure returns (bytes32 digest); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`spender`|`address`|The address which may spend the tokenId| |`tokenId`|`uint256`|The tokenId of the owner, which may be spent by spender| |`nonce`|`uint256`|A unique non-ordered value for each signature to prevent replay attacks| |`deadline`|`uint256`|The time at which the signature expires| **Returns** |Name|Type|Description| |----|----|-----------| |`digest`|`bytes32`|The hash of the data to be signed; the equivalent to keccak256(abi.encode(PERMIT_TYPEHASH, spender, tokenId, nonce, deadline));| ### hashPermitForAll Hashes the data that will be signed for IERC721Permit_v4.permit() ```solidity function hashPermitForAll(address operator, bool approved, uint256 nonce, uint256 deadline) internal pure returns (bytes32 digest); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`operator`|`address`|The address which may spend any of the owner's tokenIds| |`approved`|`bool`|true if the operator is to have full permission over the owner's tokenIds; false otherwise| |`nonce`|`uint256`|A unique non-ordered value for each signature to prevent replay attacks| |`deadline`|`uint256`|The time at which the signature expires| **Returns** |Name|Type|Description| |----|----|-----------| |`digest`|`bytes32`|The hash of the data to be signed; the equivalent to keccak256(abi.encode(PERMIT_FOR_ALL_TYPEHASH, operator, approved, nonce, deadline));| --- ## HexStrings [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/HexStrings.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Provides function for converting numbers to hexadecimal strings *Reference: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/HexStrings.sol* ## State Variables ### ALPHABET ```solidity bytes16 internal constant ALPHABET = "0123456789abcdef"; ``` ## Functions ### toHexStringNoPrefix Convert a number to a hex string without the '0x' prefix with a fixed length ```solidity function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`value`|`uint256`|The number to convert| |`length`|`uint256`|The length of the output string, starting from the last character of the string| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The hex string| --- ## LiquidityAmounts(Libraries) [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/LiquidityAmounts.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Provides functions for computing liquidity amounts from token amounts and prices ## Functions ### getLiquidityForAmount0 Computes the amount of liquidity received for a given amount of token0 and price range *Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))* ```solidity function getLiquidityForAmount0(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint256 amount0) internal pure returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceAX96`|`uint160`|A sqrt price representing the first tick boundary| |`sqrtPriceBX96`|`uint160`|A sqrt price representing the second tick boundary| |`amount0`|`uint256`|The amount0 being sent in| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The amount of returned liquidity| ### getLiquidityForAmount1 Computes the amount of liquidity received for a given amount of token1 and price range *Calculates amount1 / (sqrt(upper) - sqrt(lower)).* ```solidity function getLiquidityForAmount1(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint256 amount1) internal pure returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceAX96`|`uint160`|A sqrt price representing the first tick boundary| |`sqrtPriceBX96`|`uint160`|A sqrt price representing the second tick boundary| |`amount1`|`uint256`|The amount1 being sent in| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The amount of returned liquidity| ### getLiquidityForAmounts Computes the maximum amount of liquidity received for a given amount of token0, token1, the current pool prices and the prices at the tick boundaries ```solidity function getLiquidityForAmounts( uint160 sqrtPriceX96, uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint256 amount0, uint256 amount1 ) internal pure returns (uint128 liquidity); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sqrtPriceX96`|`uint160`|A sqrt price representing the current pool prices| |`sqrtPriceAX96`|`uint160`|A sqrt price representing the first tick boundary| |`sqrtPriceBX96`|`uint160`|A sqrt price representing the second tick boundary| |`amount0`|`uint256`|The amount of token0 being sent in| |`amount1`|`uint256`|The amount of token1 being sent in| **Returns** |Name|Type|Description| |----|----|-----------| |`liquidity`|`uint128`|The maximum amount of liquidity received| --- ## Locker [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/Locker.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) This is a temporary library that allows us to use transient storage (tstore/tload) TODO: This library can be deleted when we have the transient keyword support in solidity. ## State Variables ### LOCKED_BY_SLOT ```solidity bytes32 constant LOCKED_BY_SLOT = 0x0aedd6bde10e3aa2adec092b02a3e3e805795516cda41f27aa145b8f300af87a; ``` ## Functions ### set ```solidity function set(address locker) internal; ``` ### get ```solidity function get() internal view returns (address locker); ``` --- ## PathKey [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/PathKey.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ```solidity struct PathKey { Currency intermediateCurrency; uint24 fee; int24 tickSpacing; IHooks hooks; bytes hookData; } ``` ## PathKeyLibrary [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/PathKey.sol) Functions for working with PathKeys ## Functions ### getPoolAndSwapDirection Get the pool and swap direction for a given PathKey ```solidity function getPoolAndSwapDirection(PathKey calldata params, Currency currencyIn) internal pure returns (PoolKey memory poolKey, bool zeroForOne); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`PathKey`|the given PathKey| |`currencyIn`|`Currency`|the input currency| **Returns** |Name|Type|Description| |----|----|-----------| |`poolKey`|`PoolKey`|the pool key of the swap| |`zeroForOne`|`bool`|the direction of the swap, true if currency0 is being swapped for currency1| --- ## PositionConfig [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/PositionConfig.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ```solidity struct PositionConfig { PoolKey poolKey; int24 tickLower; int24 tickUpper; } ``` ## PositionConfigLibrary [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/PositionConfig.sol) Library to calculate the PositionConfigId from the PositionConfig struct ## Functions ### toId ```solidity function toId(PositionConfig calldata config) internal pure returns (bytes32 id); ``` --- ## PositionConfigId [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/PositionConfigId.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) A configId is set per tokenId The lower 255 bits are used to store the truncated hash of the corresponding PositionConfig The upper bit is used to signal if the tokenId has a subscriber ```solidity struct PositionConfigId { bytes32 id; } ``` ## PositionConfigIdLibrary [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/PositionConfigId.sol) ## State Variables ### MASK_UPPER_BIT ```solidity bytes32 constant MASK_UPPER_BIT = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; ``` ### DIRTY_UPPER_BIT ```solidity bytes32 constant DIRTY_UPPER_BIT = 0x8000000000000000000000000000000000000000000000000000000000000000; ``` ## Functions ### getConfigId returns the truncated hash of the PositionConfig for a given tokenId ```solidity function getConfigId(PositionConfigId storage _configId) internal view returns (bytes32 configId); ``` ### setConfigId *We only set the config on mint, guaranteeing that the most significant bit is unset, so we can just assign the entire 32 bytes to the id.* ```solidity function setConfigId(PositionConfigId storage _configId, bytes32 configId) internal; ``` ### setSubscribe ```solidity function setSubscribe(PositionConfigId storage configId) internal; ``` ### setUnsubscribe ```solidity function setUnsubscribe(PositionConfigId storage configId) internal; ``` ### hasSubscriber ```solidity function hasSubscriber(PositionConfigId storage configId) internal view returns (bool subscribed); ``` --- ## PositionInfo [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/PositionInfoLibrary.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) *PositionInfo is a packed version of solidity structure. Using the packaged version saves gas and memory by not storing the structure fields in memory slots. Layout: 200 bits poolId | 24 bits tickUpper | 24 bits tickLower | 8 bits hasSubscriber Fields in the direction from the least significant bit: A flag to know if the tokenId is subscribed to an address uint8 hasSubscriber; The tickUpper of the position int24 tickUpper; The tickLower of the position int24 tickLower; The truncated poolId. Truncates a bytes32 value so the most signifcant (highest) 200 bits are used. bytes25 poolId; Note: If more bits are needed, hasSubscriber can be a single bit.* ```solidity type PositionInfo is uint256; ``` ## PositionInfoLibrary [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/PositionInfoLibrary.sol) ## State Variables ### EMPTY_POSITION_INFO ```solidity PositionInfo internal constant EMPTY_POSITION_INFO = PositionInfo.wrap(0); ``` ### MASK_UPPER_200_BITS ```solidity uint256 internal constant MASK_UPPER_200_BITS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000; ``` ### MASK_8_BITS ```solidity uint256 internal constant MASK_8_BITS = 0xFF; ``` ### MASK_24_BITS ```solidity uint24 internal constant MASK_24_BITS = 0xFFFFFF; ``` ### SET_UNSUBSCRIBE ```solidity uint256 internal constant SET_UNSUBSCRIBE = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00; ``` ### SET_SUBSCRIBE ```solidity uint256 internal constant SET_SUBSCRIBE = 0x01; ``` ### TICK_LOWER_OFFSET ```solidity uint8 internal constant TICK_LOWER_OFFSET = 8; ``` ### TICK_UPPER_OFFSET ```solidity uint8 internal constant TICK_UPPER_OFFSET = 32; ``` ## Functions ### poolId *This poolId is NOT compatible with the poolId used in UniswapV4 core. It is truncated to 25 bytes, and just used to lookup PoolKey in the poolKeys mapping.* ```solidity function poolId(PositionInfo info) internal pure returns (bytes25 _poolId); ``` ### tickLower ```solidity function tickLower(PositionInfo info) internal pure returns (int24 _tickLower); ``` ### tickUpper ```solidity function tickUpper(PositionInfo info) internal pure returns (int24 _tickUpper); ``` ### hasSubscriber ```solidity function hasSubscriber(PositionInfo info) internal pure returns (bool _hasSubscriber); ``` ### setSubscribe *this does not actually set any storage* ```solidity function setSubscribe(PositionInfo info) internal pure returns (PositionInfo _info); ``` ### setUnsubscribe *this does not actually set any storage* ```solidity function setUnsubscribe(PositionInfo info) internal pure returns (PositionInfo _info); ``` ### initialize Creates the default PositionInfo struct *Called when minting a new position* ```solidity function initialize(PoolKey memory _poolKey, int24 _tickLower, int24 _tickUpper) internal pure returns (PositionInfo info); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`_poolKey`|`PoolKey`|the pool key of the position| |`_tickLower`|`int24`|the lower tick of the position| |`_tickUpper`|`int24`|the upper tick of the position| **Returns** |Name|Type|Description| |----|----|-----------| |`info`|`PositionInfo`|packed position info, with the truncated poolId and the hasSubscriber flag set to false| --- ## QuoterRevert [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/QuoterRevert.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) ## Functions ### revertQuote reverts, where the revert data is the provided bytes *called when quoting, to record the quote amount in an error* *QuoteSwap is used to differentiate this error from other errors thrown when simulating the swap* ```solidity function revertQuote(uint256 quoteAmount) internal pure; ``` ### bubbleReason reverts using the revertData as the reason *to bubble up both the valid QuoteSwap(amount) error, or an alternative error thrown during simulation* ```solidity function bubbleReason(bytes memory revertData) internal pure; ``` ### parseQuoteAmount validates whether a revert reason is a valid swap quote or not if valid, it decodes the quote to return. Otherwise it reverts. ```solidity function parseQuoteAmount(bytes memory reason) internal pure returns (uint256 quoteAmount); ``` ## Errors ### UnexpectedRevertBytes error thrown when invalid revert bytes are thrown by the quote ```solidity error UnexpectedRevertBytes(bytes revertData); ``` ### QuoteSwap error thrown containing the quote as the data, to be caught and parsed later ```solidity error QuoteSwap(uint256 amount); ``` --- ## SVG [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/SVG.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) Provides a function for generating an SVG associated with a Uniswap NFT *Reference: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/NFTSVG.sol* ## State Variables ### curve1 ```solidity string constant curve1 = "M1 1C41 41 105 105 145 145"; ``` ### curve2 ```solidity string constant curve2 = "M1 1C33 49 97 113 145 145"; ``` ### curve3 ```solidity string constant curve3 = "M1 1C33 57 89 113 145 145"; ``` ### curve4 ```solidity string constant curve4 = "M1 1C25 65 81 121 145 145"; ``` ### curve5 ```solidity string constant curve5 = "M1 1C17 73 73 129 145 145"; ``` ### curve6 ```solidity string constant curve6 = "M1 1C9 81 65 137 145 145"; ``` ### curve7 ```solidity string constant curve7 = "M1 1C1 89 57.5 145 145 145"; ``` ### curve8 ```solidity string constant curve8 = "M1 1C1 97 49 145 145 145"; ``` ## Functions ### generateSVG Generate the SVG associated with a Uniswap v4 NFT ```solidity function generateSVG(SVGParams memory params) internal pure returns (string memory svg); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`SVGParams`|The SVGParams struct containing the parameters for the SVG| **Returns** |Name|Type|Description| |----|----|-----------| |`svg`|`string`|The SVG string associated with the NFT| ### generateSVGDefs Generate the SVG defs that create the color scheme for the SVG ```solidity function generateSVGDefs(SVGParams memory params) private pure returns (string memory svg); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`params`|`SVGParams`|The SVGParams struct containing the parameters to generate the SVG defs| **Returns** |Name|Type|Description| |----|----|-----------| |`svg`|`string`|The SVG defs string| ### generateSVGBorderText Generate the SVG for the moving border text displaying the quote and base currency addresses with their symbols ```solidity function generateSVGBorderText( string memory quoteCurrency, string memory baseCurrency, string memory quoteCurrencySymbol, string memory baseCurrencySymbol ) private pure returns (string memory svg); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`quoteCurrency`|`string`|The quote currency| |`baseCurrency`|`string`|The base currency| |`quoteCurrencySymbol`|`string`|The quote currency symbol| |`baseCurrencySymbol`|`string`|The base currency symbol| **Returns** |Name|Type|Description| |----|----|-----------| |`svg`|`string`|The SVG for the border NFT's border text| ### generateSVGCardMantle Generate the SVG for the card mantle displaying the quote and base currency symbols and fee tier ```solidity function generateSVGCardMantle( string memory quoteCurrencySymbol, string memory baseCurrencySymbol, string memory feeTier ) private pure returns (string memory svg); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`quoteCurrencySymbol`|`string`|The quote currency symbol| |`baseCurrencySymbol`|`string`|The base currency symbol| |`feeTier`|`string`|The fee tier| **Returns** |Name|Type|Description| |----|----|-----------| |`svg`|`string`|The SVG for the card mantle| ### generageSvgCurve Generate the SVG for the curve that represents the position. Fade up (top is faded) if current price is above your position range, fade down (bottom is faded) if current price is below your position range Circles are generated at the ends of the curve if the position is in range, or at one end of the curve it is on if not in range ```solidity function generageSvgCurve(int24 tickLower, int24 tickUpper, int24 tickSpacing, int8 overRange) private pure returns (string memory svg); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tickLower`|`int24`|The lower tick| |`tickUpper`|`int24`|The upper tick| |`tickSpacing`|`int24`|The tick spacing| |`overRange`|`int8`|Whether the current tick is in range, over range, or under range| **Returns** |Name|Type|Description| |----|----|-----------| |`svg`|`string`|The SVG for the curve| ### getCurve Get the curve based on the tick range The smaller the tick range, the smaller/more linear the curve ```solidity function getCurve(int24 tickLower, int24 tickUpper, int24 tickSpacing) internal pure returns (string memory curve); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tickLower`|`int24`|The lower tick| |`tickUpper`|`int24`|The upper tick| |`tickSpacing`|`int24`|The tick spacing| **Returns** |Name|Type|Description| |----|----|-----------| |`curve`|`string`|The curve path| ### generateSVGCurveCircle Generate the SVG for the circles on the curve ```solidity function generateSVGCurveCircle(int8 overRange) internal pure returns (string memory svg); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`overRange`|`int8`|0 if the current tick is in range, 1 if the current tick is over range, -1 if the current tick is under range| **Returns** |Name|Type|Description| |----|----|-----------| |`svg`|`string`|The SVG for the circles| ### generateSVGPositionDataAndLocationCurve If the position is over or under range, generate one circle at the end of the curve on the side of the range it is on with a larger circle around it If the position is in range, generate two circles at the ends of the curve Generate the SVG for the position data (token ID, hooks address, min tick, max tick) and the location curve (where your position falls on the curve) ```solidity function generateSVGPositionDataAndLocationCurve(string memory tokenId, address hook, int24 tickLower, int24 tickUpper) private pure returns (string memory svg); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`string`|The token ID| |`hook`|`address`|The hooks address| |`tickLower`|`int24`|The lower tick| |`tickUpper`|`int24`|The upper tick| **Returns** |Name|Type|Description| |----|----|-----------| |`svg`|`string`|The SVG for the position data and location curve| ### substring ```solidity function substring(string memory str, uint256 startIndex, uint256 endIndex) internal pure returns (string memory); ``` ### tickToString ```solidity function tickToString(int24 tick) private pure returns (string memory); ``` ### rangeLocation Get the location of where your position falls on the curve ```solidity function rangeLocation(int24 tickLower, int24 tickUpper) internal pure returns (string memory, string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tickLower`|`int24`|The lower tick| |`tickUpper`|`int24`|The upper tick| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|The x and y coordinates of the location of the liquidity| |``|`string`|| ### generateSVGRareSparkle Generates the SVG for a rare sparkle if the NFT is rare. Else, returns an empty string ```solidity function generateSVGRareSparkle(uint256 tokenId, address hooks) private pure returns (string memory svg); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|The token ID| |`hooks`|`address`|The hooks address| **Returns** |Name|Type|Description| |----|----|-----------| |`svg`|`string`|The SVG for the rare sparkle| ### isRare Determines if an NFT is rare based on the token ID and hooks address ```solidity function isRare(uint256 tokenId, address hooks) internal pure returns (bool); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|The token ID| |`hooks`|`address`|The hooks address| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bool`|Whether the NFT is rare or not| ## Structs ### SVGParams ```solidity struct SVGParams { string quoteCurrency; string baseCurrency; address hooks; string quoteCurrencySymbol; string baseCurrencySymbol; string feeTier; int24 tickLower; int24 tickUpper; int24 tickSpacing; int8 overRange; uint256 tokenId; string color0; string color1; string color2; string color3; string x1; string y1; string x2; string y2; string x3; string y3; } ``` --- ## SafeCurrencyMetadata [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/SafeCurrencyMetadata.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) can produce symbols and decimals from inconsistent or absent ERC20 implementations *Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol* ## State Variables ### MAX_SYMBOL_LENGTH ```solidity uint8 constant MAX_SYMBOL_LENGTH = 12; ``` ## Functions ### currencySymbol attempts to extract the currency symbol. if it does not implement symbol, returns a symbol derived from the address ```solidity function currencySymbol(address currency, string memory nativeLabel) internal view returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`address`|The currency address| |`nativeLabel`|`string`|The native label| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|the currency symbol| ### currencyDecimals attempts to extract the token decimals, returns 0 if not implemented or not a uint8 ```solidity function currencyDecimals(address currency) internal view returns (uint8); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currency`|`address`|The currency address| **Returns** |Name|Type|Description| |----|----|-----------| |``|`uint8`|the currency decimals| ### bytes32ToString ```solidity function bytes32ToString(bytes32 x) private pure returns (string memory); ``` ### addressToSymbol produces a symbol from the address - the first 6 hex of the address string in upper case ```solidity function addressToSymbol(address currencyAddress) private pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currencyAddress`|`address`|the address of the currency| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|the symbol| ### callAndParseStringReturn calls an external view contract method that returns a symbol, and parses the output into a string ```solidity function callAndParseStringReturn(address currencyAddress, bytes4 selector) private view returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`currencyAddress`|`address`|the address of the currency| |`selector`|`bytes4`|the selector of the symbol method| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|the symbol| ### truncateSymbol truncates the symbol to the MAX_SYMBOL_LENGTH *assumes the string is already longer than MAX_SYMBOL_LENGTH (or the same)* ```solidity function truncateSymbol(string memory str) internal pure returns (string memory); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`str`|`string`|the symbol| **Returns** |Name|Type|Description| |----|----|-----------| |``|`string`|the truncated symbol| --- ## SlippageCheck [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/SlippageCheck.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) a library for checking if a delta exceeds a maximum ceiling or fails to meet a minimum floor ## Functions ### validateMinOut Revert if one or both deltas does not meet a minimum output *This should be called when removing liquidity (burn or decrease)* ```solidity function validateMinOut(BalanceDelta delta, uint128 amount0Min, uint128 amount1Min) internal pure; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`delta`|`BalanceDelta`|The principal amount of tokens to be removed, does not include any fees accrued| |`amount0Min`|`uint128`|The minimum amount of token0 to receive| |`amount1Min`|`uint128`|The minimum amount of token1 to receive| ### validateMaxIn Revert if one or both deltas exceeds a maximum input *This should be called when adding liquidity (mint or increase)* ```solidity function validateMaxIn(BalanceDelta delta, uint128 amount0Max, uint128 amount1Max) internal pure; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`delta`|`BalanceDelta`|The principal amount of tokens to be added, does not include any fees accrued (which is possible on increase)| |`amount0Max`|`uint128`|The maximum amount of token0 to spend| |`amount1Max`|`uint128`|The maximum amount of token1 to spend| ## Errors ### MaximumAmountExceeded ```solidity error MaximumAmountExceeded(uint128 maximumAmount, uint128 amountRequested); ``` ### MinimumAmountInsufficient ```solidity error MinimumAmountInsufficient(uint128 minimumAmount, uint128 amountReceived); ``` --- ## VanityAddressLib [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/libraries/VanityAddressLib.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) A library to score addresses based on their vanity ## Functions ### betterThan Compares two addresses and returns true if the first address has a better vanity score ```solidity function betterThan(address first, address second) internal pure returns (bool better); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`first`|`address`|The first address to compare| |`second`|`address`|The second address to compare| **Returns** |Name|Type|Description| |----|----|-----------| |`better`|`bool`|True if the first address has a better vanity score| ### score Scores an address based on its vanity *Scoring rules: Requirement: The first nonzero nibble must be 4 10 points for every leading 0 nibble 40 points if the first 4 is followed by 3 more 4s 20 points if the first nibble after the 4 4s is NOT a 4 20 points if the last 4 nibbles are 4s 1 point for every 4* ```solidity function score(address addr) internal pure returns (uint256 calculatedScore); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`addr`|`address`|The address to score| **Returns** |Name|Type|Description| |----|----|-----------| |`calculatedScore`|`uint256`|The vanity score of the address| ### getLeadingNibbleCount Returns the number of leading nibbles in an address that match a given value ```solidity function getLeadingNibbleCount(bytes20 addrBytes, uint256 startIndex, uint8 comparison) internal pure returns (uint256 count); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`addrBytes`|`bytes20`|The address to count the leading zero nibbles in| |`startIndex`|`uint256`|| |`comparison`|`uint8`|| ### getNibble Returns the nibble at a given index in an address ```solidity function getNibble(bytes20 input, uint256 nibbleIndex) internal pure returns (uint8 currentNibble); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`input`|`bytes20`|The address to get the nibble from| |`nibbleIndex`|`uint256`|The index of the nibble to get| --- ## BaseHook [Git Source](https://github.com/uniswap/v4-periphery/blob/ea2bf2e1ba6863bb809fc2ff791744f308c4a26d/src/utils/BaseHook.sol) - Generated with [forge doc](https://book.getfoundry.sh/reference/forge/forge-doc) **Inherits:** IHooks, [ImmutableState](contracts/v4/reference/periphery/base/ImmutableState.md) abstract contract for hook implementations ## Functions ### constructor ```solidity constructor(IPoolManager _manager) ImmutableState(_manager); ``` ### getHookPermissions Returns a struct of permissions to signal which hook functions are to be implemented *Used at deployment to validate the address correctly represents the expected permissions* ```solidity function getHookPermissions() public pure virtual returns (Hooks.Permissions memory); ``` ### validateHookAddress Validates the deployed hook address agrees with the expected permissions of the hook *this function is virtual so that we can override it during testing, which allows us to deploy an implementation to any address and then etch the bytecode into the correct address* ```solidity function validateHookAddress(BaseHook _this) internal pure virtual; ``` ### beforeInitialize The hook called before the state of a pool is initialized ```solidity function beforeInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96) external onlyPoolManager returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the initialize call| |`key`|`PoolKey`|The key for the pool being initialized| |`sqrtPriceX96`|`uint160`|The sqrt(price) of the pool as a Q64.96| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### _beforeInitialize ```solidity function _beforeInitialize(address, PoolKey calldata, uint160) internal virtual returns (bytes4); ``` ### afterInitialize The hook called after the state of a pool is initialized ```solidity function afterInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96, int24 tick) external onlyPoolManager returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the initialize call| |`key`|`PoolKey`|The key for the pool being initialized| |`sqrtPriceX96`|`uint160`|The sqrt(price) of the pool as a Q64.96| |`tick`|`int24`|The current tick after the state of a pool is initialized| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### _afterInitialize ```solidity function _afterInitialize(address, PoolKey calldata, uint160, int24) internal virtual returns (bytes4); ``` ### beforeAddLiquidity The hook called before liquidity is added ```solidity function beforeAddLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, bytes calldata hookData ) external onlyPoolManager returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the add liquidity call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for adding liquidity| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the liquidity provider to be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### _beforeAddLiquidity ```solidity function _beforeAddLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata) internal virtual returns (bytes4); ``` ### beforeRemoveLiquidity The hook called before liquidity is removed ```solidity function beforeRemoveLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, bytes calldata hookData ) external onlyPoolManager returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the remove liquidity call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for removing liquidity| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the liquidity provider to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### _beforeRemoveLiquidity ```solidity function _beforeRemoveLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata) internal virtual returns (bytes4); ``` ### afterAddLiquidity The hook called after liquidity is added ```solidity function afterAddLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, BalanceDelta delta, BalanceDelta feesAccrued, bytes calldata hookData ) external onlyPoolManager returns (bytes4, BalanceDelta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the add liquidity call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for adding liquidity| |`delta`|`BalanceDelta`|The caller's balance delta after adding liquidity; the sum of principal delta, fees accrued, and hook delta| |`feesAccrued`|`BalanceDelta`|The fees accrued since the last time fees were collected from this position| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the liquidity provider to be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| |``|`BalanceDelta`|BalanceDelta The hook's delta in token0 and token1. Positive: the hook is owed/took currency, negative: the hook owes/sent currency| ### _afterAddLiquidity ```solidity function _afterAddLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata ) internal virtual returns (bytes4, BalanceDelta); ``` ### afterRemoveLiquidity The hook called after liquidity is removed ```solidity function afterRemoveLiquidity( address sender, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params, BalanceDelta delta, BalanceDelta feesAccrued, bytes calldata hookData ) external onlyPoolManager returns (bytes4, BalanceDelta); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the remove liquidity call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.ModifyLiquidityParams`|The parameters for removing liquidity| |`delta`|`BalanceDelta`|The caller's balance delta after removing liquidity; the sum of principal delta, fees accrued, and hook delta| |`feesAccrued`|`BalanceDelta`|The fees accrued since the last time fees were collected from this position| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the liquidity provider to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| |``|`BalanceDelta`|BalanceDelta The hook's delta in token0 and token1. Positive: the hook is owed/took currency, negative: the hook owes/sent currency| ### _afterRemoveLiquidity ```solidity function _afterRemoveLiquidity( address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata ) internal virtual returns (bytes4, BalanceDelta); ``` ### beforeSwap The hook called before a swap ```solidity function beforeSwap( address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata hookData ) external onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the swap call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.SwapParams`|The parameters for the swap| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the swapper to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| |``|`BeforeSwapDelta`|BeforeSwapDelta The hook's delta in specified and unspecified currencies. Positive: the hook is owed/took currency, negative: the hook owes/sent currency| |``|`uint24`|uint24 Optionally override the lp fee, only used if three conditions are met: 1. the Pool has a dynamic fee, 2. the value's 2nd highest bit is set (23rd bit, 0x400000), and 3. the value is less than or equal to the maximum fee (1 million)| ### _beforeSwap ```solidity function _beforeSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata) internal virtual returns (bytes4, BeforeSwapDelta, uint24); ``` ### afterSwap The hook called after a swap ```solidity function afterSwap( address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, BalanceDelta delta, bytes calldata hookData ) external onlyPoolManager returns (bytes4, int128); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the swap call| |`key`|`PoolKey`|The key for the pool| |`params`|`IPoolManager.SwapParams`|The parameters for the swap| |`delta`|`BalanceDelta`|The amount owed to the caller (positive) or owed to the pool (negative)| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the swapper to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| |``|`int128`|int128 The hook's delta in unspecified currency. Positive: the hook is owed/took currency, negative: the hook owes/sent currency| ### _afterSwap ```solidity function _afterSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata) internal virtual returns (bytes4, int128); ``` ### beforeDonate The hook called before donate ```solidity function beforeDonate(address sender, PoolKey calldata key, uint256 amount0, uint256 amount1, bytes calldata hookData) external onlyPoolManager returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the donate call| |`key`|`PoolKey`|The key for the pool| |`amount0`|`uint256`|The amount of token0 being donated| |`amount1`|`uint256`|The amount of token1 being donated| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the donor to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### _beforeDonate ```solidity function _beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) internal virtual returns (bytes4); ``` ### afterDonate The hook called after donate ```solidity function afterDonate(address sender, PoolKey calldata key, uint256 amount0, uint256 amount1, bytes calldata hookData) external onlyPoolManager returns (bytes4); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`sender`|`address`|The initial msg.sender for the donate call| |`key`|`PoolKey`|The key for the pool| |`amount0`|`uint256`|The amount of token0 being donated| |`amount1`|`uint256`|The amount of token1 being donated| |`hookData`|`bytes`|Arbitrary data handed into the PoolManager by the donor to be be passed on to the hook| **Returns** |Name|Type|Description| |----|----|-----------| |``|`bytes4`|bytes4 The function selector for the hook| ### _afterDonate ```solidity function _afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) internal virtual returns (bytes4); ``` ## Errors ### HookNotImplemented ```solidity error HookNotImplemented(); ``` --- ## Security Framework # A Self-Directed Security Framework The aim of this guide, and its accompanying [worksheet](https://docs.google.com/spreadsheets/d/1oZdKZh13UbqVp3HujAcv-2NKP-j7NQAELhB7kyWlLRE/edit?usp=sharing), is to give hook developers a clear, structured, and self-directed framework for understanding the security risks inherent to their project. It explains how complexity, math, external dependencies, upgradeability, liquidity behavior, and other factors contribute to risk. It also provides a consistent rubric for classifying risk tiers and choosing an appropriate combination of audits, monitoring services, bug bounties, and optional formal verification. This [framework](https://github.com/uniswapfoundation/security-framework/tree/main) is provided as a public, informational resource. The [Uniswap Foundation](https://www.uniswapfoundation.org/) does not review, audit, or certify any submissions, scores, or implementations derived from it. Use of this framework is voluntary and self-directed. Scores and categorizations do **not** represent security assurances or guarantees of safety; they are intended only to outline recommended practices that may help reduce risk. References to “required,” “minimum,” or “mandatory” standards are descriptive, not prescriptive, and should not be interpreted as formal validation or endorsement. # Guide Overview The guide consists of 12 sections. Here is a brief overview of each section and what you will find inside: 1. **[Understanding Hook Risk in v4:](/contracts/v4/security#1-understanding-hook-risk-in-v4)** Explains how hooks can introduce new security risks through accounting, math, external dependencies, governance, and liquidity behaviors. 2. [Hook Risk Self-Scoring Dimensions:](/contracts/v4/security#2-hook-risk-self-scoring-dimensions) Defines the nine quantitative dimensions used to evaluate a hook’s inherent risk profile. 3. [Generic Hook Risk Tiers & Recommendations:](/contracts/v4/security#3-generic-hook-risk-tiers-and-recommendations) Maps total scores to low, medium, and high risk tiers and outlines the minimum security expectations for each. 4. [Feature-Specific Security Recommendations:](/contracts/v4/security#4-feature-specific-security-recommendations) Highlights seven high-impact features that trigger mandatory security actions regardless of overall tier score. 5. [How to Evaluate Your Security Needs:](/contracts/v4/security#5-how-to-evaluate-your-security-needs) Explains how to combine risk tiers and feature triggers into an actionable security plan for your hook. 6. [Operational Security (OPSEC):](/contracts/v4/security#6-operational-security-opsec) Focuses on protecting the processes, information, people, and workflows around your hook, ensuring attackers cannot exploit how you operate, not just how your code behaves. 7. [Security Best Practices Checklist:](/contracts/v4/security#7-security-best-practices-checklist) Provides universal best-practice guidelines that all hooks should follow across accounting, access control, upgradeability, and transparency. 8. [Risk Calculator Scoring Sheet:](/contracts/v4/security#8-risk-calculator-scoring-sheet) Introduces an automated worksheet to calculate scores, identify triggers, and generate tailored recommendations. 9. [How To Use This Framework:](/contracts/v4/security#9-how-to-use-this-framework) Outlines a step-by-step workflow for scoring your hook, interpreting results, and preparing for audits and deployment. 10. [Future Extensions Community Driven:](/contracts/v4/security#10-future-extensions-community-driven) Describes how the community can evolve the framework by adding dimensions, patterns, tools, and shared learnings. 11. [Security Resources:](/contracts/v4/security#11-security-resources) a list of external resources for Hook builders. 12. [Conclusion:](/contracts/v4/security#12-conclusion) Summarizes the framework’s purpose and reinforces the importance of self-directed, ongoing security responsibility for hook developers. # How to Use the Guide + Worksheet This guide begins with an overview of hook security risk and the key dimensions used to evaluate it. Teams can go through the process more easily using the [Uniswap Hooks Security Worksheet](https://docs.google.com/spreadsheets/d/1oZdKZh13UbqVp3HujAcv-2NKP-j7NQAELhB7kyWlLRE/edit?usp=sharing), which automates scoring and generates recommendations based on both tier and feature triggers. Together, these resources help teams understand their risk profile, identify required safeguards, and plan audits and monitoring before deployment. By the end of this process, teams should have: - A clear understanding of the risks present in their hook design - A completed risk score and feature trigger assessment - A list of required and recommended security actions tailored to their hook - A high-level overview of operational security considerations - A documented plan for audits, testing, monitoring, and transparency This self-assessment serves as a foundation for safe development, responsible deployment, and long term maintenance of Uniswap v4 hooks. ## 1. Understanding Hook Risk in v4 Uniswap v4 introduces hooks, comprised of arbitrary smart contract logic that executes at critical lifecycle points such as swaps, liquidity changes, and donations. Hooks dramatically expand what can be built on top of the AMM: programmable fees, custom curves, lending integrations, dynamic risk controls, and more. But with this flexibility comes a new category of security risks that differ materially from both previous Uniswap versions and traditional DeFi contract architectures. Hooks sit inside the core swap execution pipeline; incorrect assumptions or unsafe patterns can compromise pool integrity, price trades incorrectly, or expose liquidity providers to losses. This section summarizes the major classes of risk that commonly arise in hook development. Understanding these patterns early helps teams design safer mechanisms, write better tests, and scope appropriate audits. ### How Hook Risk Emerges Hook risk tends to arise through several common patterns. Understanding these early in the design process helps teams apply the correct mitigations. ### Accounting & Token Handling Failures Hooks that touch balances or modify deltas must handle accounting with extreme precision, rounding in the correct direction to prevent losses. Small errors can cascade into large systemic failures. Key risks include: - **Incorrect delta handling:** Computing input/output amounts incorrectly, failing to validate internal balance changes, or relying on stale deltas between callbacks. - **ERC20 behavioral assumptions:** Many tokens deviate from the “standard”: fee-on-transfer tokens, rebasing tokens, ERC-777 hooks, or pausable/ freezable tokens. These can silently break accounting, introduce reentrancy, or cause unexpected DoS behavior. - **Holding or rehypothecating assets:** When a hook holds liquidity itself or deposits assets into another protocol, rounding errors and token-return inconsistencies multiply quickly. :::info Although the well-known [Bunni](https://www.halborn.com/blog/post/explained-the-bunni-hack-september-2025) and [Balancer](https://www.halborn.com/blog/post/explained-the-balancer-hack-november-2025) incidents were not specifically related to v4 infrastructure, they illustrate the types of accounting drift and asset-handling mistakes that hook developers must proactively guard against. ::: ### External Calls, Reentrancy, and State Drift Any external call made during `beforeSwap`, `afterSwap`, or liquidity callbacks reopens the entire execution environment. This invalidates assumptions about atomicity and order. Teams must test and reason about: - **Reentrancy into the same pool:** Nested callbacks can overwrite internal state or read partially updated values. - **Reentrancy into other pools using the same hook:** Shared storage and shared assumptions may break unexpectedly. - **External protocol state drift:** Prices, balances, positions, or interest rates can change between callbacks, even within the same transaction. - **Liquidity migration between callbacks:** External calls can add/remove liquidity, making earlier snapshots invalid. :::info **Core principle:** If your hook calls external contracts, assume *every* piece of state may change before the callback sequence completes. Only explicit tests can validate safety here. ::: ### Mathematical Correctness & Precision Risks Non-standard math is one of the most frequent sources of severe DeFi exploits. Hooks may define custom curves, implement TWAMM-style flows, modify fees dynamically, or use fixed-point operations. Common failure modes include: - **Precision drift & rounding instability:** Small rounding errors can accumulate into large pricing deviations or accounting imbalances. - **Input-range or domain violations:** Division by zero, negative ratios, overflow, or taking a root/log outside a valid domain. - **Fixed-point arithmetic instability:** Exponentiation, logarithms, fractional powers, or operations that mix Q96/Q128 formats can amplify tiny numeric errors. - **Invariant discontinuities:** Piecewise or hybrid curves may contain abrupt jumps that allow manipulation or revert paths. - **Non-invertible or inconsistent state updates:** When multiple variables must update atomically, math that isn’t perfectly reversible can de-synchronize the system. - **Cumulative drift in iterative flows:** TWAMM and streaming-swap mechanisms can diverge over time unless precision is tightly controlled. Because these issues often emerge only under adversarial flow or large TVL, a **math-specialist review is strongly recommended** for any hook performing non-trivial computation. ### External Dependency Failures Hooks that rely on oracles, lending protocols, LSTs, bridges, sequencers, or cross-chain systems inherit all of those systems’ risks. Key risks include: - stale or manipulated oracle prices - reverts or delays in external contracts - liquidity, collateral, or rate changes mid-swap - cross-chain messages arriving late, out of order, or not at all External dependencies dramatically expand the failure surface and require more thorough scenario testing. ### Upgradeability Hazards Upgradeability offers flexibility but also introduces major risks because hooks sit directly in the swap execution path. Many real-world exploits stem not from logic bugs, but from unsafe upgrade paths, compromised keys, or storage layout mistakes. Teams should avoid upgradeability unless it is truly required. **When Upgradeability Might Be Justified:** - Early-stage or experimental hooks that may need rapid iteration - Strategies requiring periodic parameter or logic updates - Deployments spanning many pools where migration costs are high Even in these cases, strict controls are necessary. **Recommended Practices:** - **Prefer immutability** when possible; it eliminates large classes of governance and operational risk. - **Use standard proxy patterns** (e.g., OpenZeppelin UUPS/Transparent) rather than custom mechanisms. - **Restrict upgrade permissions** to a multisig or timelock. Never use EOAs. - **Validate storage layout** during every upgrade to prevent slot collisions or state corruption. - **Test upgrades thoroughly**, including invariant tests and simulations of swap flows and callback ordering. - **Publish an upgrade policy** detailing how upgrades are announced, reviewed, and executed. **Versioning Instead of Upgrading:** When safety is higher priority than convenience: - Deploy new hook contracts (v1 —> v2) rather than upgrading old ones. - Allow pools to migrate voluntarily rather than forcing an upgrade path. ### Autonomous Behavior or Parameter Updates Hooks that modify parameters automatically, fees, risk buffers, curve weights, target liquidity ranges, must enforce strict guardrails: - bounded rate of change - explicit time/state gating - monotonicity constraints where relevant - complete off-chain observability Autonomous updates are a major source of emergent behavior and must be tested as adversarially as possible. ### Price Impacting Behavior and Dynamic Fees Hooks that modify swap prices, adjust fees dynamically, or override deltas directly can extract value from traders or LPs if designed poorly. Dynamic fee risks include: - Fees can be raised selectively after seeing a user’s trade, extracting value from them. - Fee changes may behave non-linearly, causing small swaps to create unexpectedly large costs. - Reentrancy or liquidity splitting can be used to manipulate how the fee is calculated. - Poorly designed fee logic can open new, unintentional MEV-style vectors. Because fee logic interacts deeply with swap math, these hooks must model worst-case adversarial flows, not just average behavior. ### TVL Growth Over Time A hook’s risk profile is not static. As liquidity grows, even small design flaws can become catastrophic. Teams must reassess: - monitoring requirements - audit depth - bug bounty scope - formal verification needs - emergency procedures whenever TVL materially increases. More info on these topics to evaluate can be found in [Generic Hook Risk Tiers & Recommendations.](/contracts/v4/security#3-generic-hook-risk-tiers-and-recommendations) ### Flash Accounting and Transient State Risks PoolManager accumulates deltas between callbacks. Hooks relying on this transient state must ensure that adversaries cannot manipulate deltas mid-execution. Incorrect assumptions about transient state can lead to: - unbalanced inputs and outputs - bypassing fee logic - value-extraction opportunities - pricing swaps incorrectly This is a hook-specific risk vector not present in previous Uniswap versions and deserves explicit design attention. ### BeforeSwapDelta and NoOp Override Risks Hooks can alter swap execution by returning `BeforeSwapDelta` values. This is an extremely powerful capability that can unintentionally introduce value-extraction paths or unbalanced token flows. Any hook exercising these permissions must be reasoned about carefully and tested extensively. Returning a `BeforeSwapDelta` that fully consumes the user’s input (or fully satisfies exact-output) means the PoolManager sees zero remaining amount, causing the Uniswap CL math to be skipped. Such cases are referred to as **NoOp swaps,** the hook effectively bypasses the pool and executes the entire trade on its own. - Hooks that routinely create NoOp swaps are essentially implementing custom trading logic or **custom curves**. - [Custom-curve hooks](https://github.com/OpenZeppelin/uniswap-hooks/blob/master/src/base/BaseCustomCurve.sol) must be extremely careful with input/output math and internal accounting, since any precision or balance error directly results in mispriced trades. - **Real-world example:** the [Bunni](https://www.halborn.com/blog/post/explained-the-bunni-hack-september-2025?utm_source=chatgpt.com) exploit involved a Uniswap v4 liquidity hook with custom curve/distribution logic that contained accounting flaws and was exploited. ### Permission Encoding & Salt Grinding Pitfalls Hooks in Uniswap v4 rely on a hashed permission system: the PoolManager derives a permission bitmap from the hook’s address and uses it to determine which callbacks (e.g., before/after swap, add liquidity, remove liquidity) are allowed. In practice this means the hook’s deployed address effectively encodes its permissions. If a CREATE2 salt is computed incorrectly when mining the hook address: - required callbacks may be disabled - undesired permissions may be unintentionally enabled - the hook’s attack surface may expand unexpectedly Teams must compute and verify the expected permission bitmap in their deployment pipeline. ### Token-Type Specific Hazards Hook assumptions may break for non-standard tokens such as: - Fee-on-transfer tokens - Rebasing or elastic-supply tokens - ERC-777 tokens with hooks - Tokens with pausability, freezing, or blacklisting capabilities - Tokens with unconventional decimals :::info *These behaviors can break accounting guarantees or introduce reentrancy risk.* ::: ### Multi-Pool and Routing Interactions Hooks may be invoked multiple times in the same transaction during routing. Developers should test: - sequential fee logic behavior - whether state assumptions hold across hops - whether autonomy or dynamic fees behave consistently - the possibility of multi-hop induced value leakage This is a unique surface area for hooks in real aggregator routing conditions. ### Cross-Chain State & Synchronization Risks Cross-chain-aware hooks face risks that do not exist in single-chain designs. Any state imported from another chain (prices, parameters, governance settings, or risk signals) always arrives with delay, may be out of order, or temporarily unavailable. Treating this data as fresh or authoritative can lead to incorrect assumptions about liquidity, pricing, or safety constraints. Because chains differ in finality, block time, and security, cross-chain messages can be delayed, replayed, dropped, or delivered multiple times. Hooks that assume synchronized or atomic state across chains risk malfunction under real network conditions. To remain safe, cross-chain hooks should incorporate: - **Conservative guardrails:** Reject stale, extreme, or unexpected updates and enforce safe parameter bounds. - **Idempotent update logic:** Ensure repeated or out-of-order messages do not corrupt state or create inconsistent transitions. - **Graceful degradation:** Safety-critical behavior should constrain itself when remote data is missing or diverging, rather than relying on optimistic assumptions. - **Off-chain observability:** Operators must detect when remote state lags, diverges, or produces suspicious sequences. - **Adversarial assumptions:** Treat the origin chain as manipulable prior to message finality and design around worst-case ordering and timing. :::info **Core message:** Cross-chain hooks must assume latency, inconsistency, and adversarial ordering as the *normal case*, not exceptions. Designing for these realities ensures hooks remain robust even when cross-chain communication behaves unpredictably. ::: ## 2. Hook Risk Self-Scoring Dimensions The risk framework assigns a score across nine dimensions. Together, these describe the hook’s complexity, potential failure surface, systemic impact, and team preparedness. These dimensions align with a [Uniswap Hooks Security Worksheet](https://docs.google.com/spreadsheets/d/1oZdKZh13UbqVp3HujAcv-2NKP-j7NQAELhB7kyWlLRE/edit?gid=1821687847#gid=1821687847) (both tools work seamlessly together). Teams should score themselves on these 9 dimensions and evaluate each dimension honestly and conservatively. Slight underestimation can lead to insufficient audits or missed safety processes ### Risk Dimensions :::note Higher means more risk ::: ### Complexity (0 to 5) Measures total code complexity including branching logic, number of callbacks, configuration patterns, and multi-step flows. Hooks with multiple operational modes or advanced features are inherently more difficult to reason about. ### Custom math (0 to 5) Includes any math that deviates from standard constant-product AMM operations, such as custom bonding curves, dynamic fee formulas, TWAMM or streaming-swap math, non-integer exponents, logarithmic or exponential functions, composite pricing functions, weighted or piecewise curves, or multi-variable state transformations. These forms of math introduce risks including rounding drift, input-range violations, discontinuities, and invariant instability. Even small numerical errors can accumulate and create mispricing, de-synchronization of balances, or execution failure. Custom math is one of the most common sources of high-severity DeFi exploits. ### External dependencies (0 to 3) Dependencies include oracles, lending markets, LST systems, bridges, cross chain flows, and off chain data sources. This increases the number of assumptions that can fail. ### External liquidity exposure (0 to 3) Hooks may move tokens to external protocols, hold their own liquidity, or participate in lending flows. The more externalized the liquidity, the higher the risk. ### TVL potential (0 to 5) Liquidity can grow after launch. Hooks intended for farms or incentivized pools should assume higher potential TVL and treat themselves accordingly. **Suggested Scoring Brackets** - **0**: <$100K (experimental or personal project) - **1:** $100K–$1M - **2:** $1M–$5M - **3:** $5M–$15M - **4:** $15M–$50M, mid-size protocol integration, or incentives - **5:** $50M+, major protocol integration, or significant incentives ### Team maturity (0 to 3) Experts with multiple production deployments and prior audits score low risk. New or anonymous teams score higher. Team maturity directly affects incident readiness and code quality. #### Suggested Scoring Brackets - **0:** **Highly Mature Team** has shipped audited production deployments or UUPS upgrades across 2+ distinct codebases, operates a mature DevOps pipeline, maintains an active incident-response process, and has demonstrated responsibility in past disclosures or upgrades. - **1:** **Experienced Team** has shipped at least one audited production deployment or upgrade, with some operational experience in handling deployments, upgrades, or maintenance, but has less breadth across multiple codebases or ecosystems. - **2:** **Moderately experienced Team** has deployed contracts to production previously but without demonstrable operational maturity. Processes for upgrades, monitoring, and incident response may be informal or ad hoc. - **3:** **Unproven team** has no prior production deployments, or deployments lacked audits and operational rigor. Team is new, anonymous, or lacks a public track record demonstrating secure coding and responsible operations. ### Upgradeability (0 to 3) Upgradeable hooks are more flexible but dangerous. Admin controls and proxy patterns introduce room for storage layout issues, logic upgrade errors, and governance attacks. ### Autonomous parameter updates (0 to 3) If a hook adjusts its own configuration automatically or based on internal state, risk increases. Autonomy amplifies misconfiguration and emergent behavior risks. ### Price impacting behavior (0 to 3) Hooks that directly influence the swap path, effective price, routing decisions, or fee structure must be treated with particular caution. ## 3. Generic Hook Risk Tiers and Recommendations Risk tiers provide a baseline set of recommendations depending on the total risk score from the previous scoring phase, but are not sufficient on their own. Certain features introduce specific risk categories that require mandatory safeguards regardless of total score. Each risk tier is expected to have extensive unit and fuzz test coverage. :::note Specific features require additional steps defined in the next section ::: ### Low Risk (0 to 6) Relatively small surface area, little or no math, no external interactions. *Minimum recommendations:* - One full audit, plus AI static analysis tools - No math specialist required - Bug bounty optional - Monitoring optional unless TVL grows significantly (see high risk hooks section for more details on monitoring) ### Medium Risk (7 to 17) Moderate complexity, custom logic, or meaningful dependencies. *Minimum recommendations:* - One full audit, plus AI static analysis tools - Optional second audit for math or complex components - Bug bounty recommended - Monitoring recommended if external dependencies exist (see high risk hooks section for more details on monitoring) - Autonomy or parameter updates may require additional test coverage ### High Risk (18 to 33) Complex math, external liquidity, autonomous behavior, upgradeability, price impact, or large TVL. *Minimum recommendations:* - Two formal audits including one by a math specialist - Mandatory bug bounty - Extended test suite recommended including invariants and stateful fuzzing *(e.g., delta conservation, fee bounds, no unintended reentrancy, monotonicity of curve functions)* - Mandatory monitoring with anomaly detection. At this tier, monitoring should aim not only to detect exploits early, but to identify silent failure modes where math or accounting begins drifting from expected behavior. Suggested monitoring targets include: - Liquidity imbalances - Delta accounting anomalies - Sudden dynamic fee spikes - Gas per swap outliers - Divergence between expected vs. actual deltas - Unusually high revert rates - Abnormal slippage patterns under small trade sizes - Deviations between theoretical curve output vs. on-chain execution - Drift in time-weighted or iterative math outputs (TWAMM, etc.) - Optional formal verification to help validate: - Invariant preservation (balance conservation, fee bounds) - Arithmetic bounds (overflow, domain violations, rounding expectations) - Reentrancy and callback-ordering assumptions - Correctness of composite math (TWAMM, curves, weighting, volatility models) ## 4. Feature-Specific Security Recommendations The following features each trigger specific security recommendations. These may overlap with your generic risk tier or may add additional requirements. It is possible a low risk hook tier may have lots of security needs based on the specific features being built. ### Custom Curve or Non Standard Math Triggers when: - Custom bonding curves or alternative invariant functions - TWAMM or streaming-swap math - Non-integer exponents or logarithms - Composite or hybrid pricing functions - Weighted, piecewise, or discontinuous curves - Any math that introduces rounding complexity or relies on strict input-range assumptions **Minimum required actions:** - At least one audit should include a math and invariants specialist - Unit tests should validate intermediate steps, edge cases, and input-range boundaries - Stateful fuzzing and invariant testing may significantly improve assurance - Formal verification is recommended if TVL score is 5 or the math materially impacts prices (Invariant preservation, arithmetic bounds, correctness of composite math) - Continuous or periodic monitoring is recommended when combined with autonomy or price-modifying behavior :::info Even if the overall score is Low or Medium, a specialized math review is recommended whenever custom math is present. ::: ### Hook Holds Its Own Liquidity or Manages External Liquidity Triggers when: - The hook temporarily or permanently holds tokens - Tokens are sent to external lending or staking protocols - Rehypothecation or collateralization occurs **Minimum required actions:** - Continuous monitoring strongly recommended - Bug bounty required if TVL score is 5 - Invariant testing required for accounting logic - At least one audit must evaluate liquidity flow correctness ### External Protocol or Oracle Dependencies Triggers when: - The hook calls external AMMs - The hook reads from oracles - The hook interacts with lending markets, LSTs, or cross chain data - Any off chain data or feed is consumed **Minimum required actions:** - Monitoring of dependency health strongly recommended - Audits must review trust assumptions, failure modes, and fallback logic - Bug bounty recommended - Scenario testing required for dependency failures - Any dependency that influences pricing requires a math or invariants review This protects against cascading failures. ### Autonomous Parameter Updates or “Self-Tuning” Hook Triggers when: - Parameters adjust automatically - Logic branches change based on internal state or on chain computation - The hook acts like a governor for itself or for the pool **Minimum required actions:** - Invariant testing required for state transitions - Monitoring strongly recommended - Two audit reviewers recommended, if combined with custom math - Upgradeability must be minimized or time-locked - Debugging and recovery procedures must be documented Even seemingly simple autonomous systems can enter unexpected states. ### Price Impacting Behavior Triggers when: - Hook modifies swap pricing - Hook inserts additional fees - Hook influences execution path - Hook adds dynamic slippage behavior - Hook injects off pool state into swap logic **Minimum required actions:** - Auditor should specialize in math - Monitoring required if TVL score is 5 - Bug bounty required - Formal verification recommended when combined with autonomy or external dependencies (Invariant preservation, arithmetic bounds, correctness of composite math, reentrancy assumptions) - Auditors should simulate worst case economic and adversarial price manipulation scenarios ### Upgradeable Triggers when: - Any form of proxy or upgradeable logic exists **Required actions:** - Storage collision review required - Documentation of upgrade policy mandatory - Time-lock or multisig control recommended - Additional audit required if using complex upgrade conditions - Monitoring recommended after each upgrade - Bug bounty required if TVL score is 5 Upgradeability expands the threat model and cannot be treated lightly. ### TVL 5 Rating (Highest Liquidity Potential) Triggers when: - TVL rating is 5, meaning the hook is intended to attract or is likely to attract significant liquidity (for example large incentives or integration in major products). **Required actions:** - Monitoring mandatory - Bug bounty mandatory - At least one high quality audit required regardless of feature set - Formal verification recommended where applicable (Invariant preservation, arithmetic bounds, correctness of composite math, reentrancy assumptions) - Liquidity limits or kill switches should be considered - Emergency response procedures required This acknowledges that impact severity scales with liquidity. ## 5. How to Evaluate your Security Needs In [section 3](/contracts/v4/security#3-generic-hook-risk-tiers-and-recommendations) we discussed the generic hook tiers and recommendations based on the risk profile of your hook. In [section 4](/contracts/v4/security#4-feature-specific-security-recommendations) the specific features you are developing were analyzed to provide more custom guidance. In practice you will need to look both at the overall risk of your hook as well as at the specific features you are developing to understand your security needs. ### Combined Trigger Logic (How it Works in Practice) The model uses **two layers**: #### Layer 1: Risk Tier (low, medium, high) General baseline audit guidance. #### Layer 2: Feature Triggers Override or upgrade requirements based on specific high-risk behaviors. This ensures teams cannot “score themselves low” while still implementing dangerous primitives such as custom curves or autonomous logic. For example: - A hook with a score of 4 but with custom math, must include an audit from a math specialist - A hook with a score of 5 but with TVL 5, must implement monitoring and bug bounty - A hook with a score of 3 but with autonomy, requires state invariant testing - A medium-risk hook with a score of 7 but has custom math and price impact, looks like a high-risk hook regardless This system is resilient to teams under-scoring themselves, because **features trigger requirements independently**. ## 6. Operational Security (OPSEC) Operational security addresses the risks that come not from code, but from the way a hook is developed, deployed, upgraded, and communicated. While audits and testing validate correctness, OPSEC ensures that sensitive information, internal processes, and privileged actions do not create unintended attack vectors. Effective OPSEC requires viewing your operations from an adversary’s perspective and minimizing what an attacker could learn or exploit. ### Critical Information and Exposure Risks Teams should identify information that could be dangerous if leaked, such as deployment targets, upgrade timing, admin key structure, internal testing results, upcoming configuration changes, and liquidity or incentive plans. Much of this information can be inferred from public repos, documentation, social media, dashboards, or on-chain patterns unless handled intentionally. ### Operational Weak Points Attackers often exploit weaknesses in processes rather than code. Risky points include upgrade procedures, oracle configuration changes, pausing mechanisms, parameter updates, and liquidity injections. Poor key management, predictable deployment habits, and overly broad internal permissions further increase exposure. ### Core OPSEC Practices OPSEC practices focus on reducing unnecessary access and limiting what information becomes public. Teams should use least-privilege permissions for keyholders, secure storage for secrets, multisig and time-locked upgrades, and controlled release processes. Communication channels used for deployments or incident response should be private and authenticated. Public documentation and upgrade notes should undergo internal review before release. ### Monitoring, Response, and Lifecycle Discipline Strong OPSEC requires monitoring not only the hook’s on-chain behavior, but also administrative and governance actions. Teams should maintain a clear incident response plan, define roles for emergency communication, and regularly test their pause or kill-switch procedures. OPSEC is continuous: reassess before deployments, before and after upgrades, as TVL grows, and whenever autonomy or new dependencies are introduced. Integrating OPSEC ensures that hooks remain secure across their entire operational lifecycle, protecting against human error, leaked information, social engineering, and process-level failures. :::info For more information see the [Operational Security framework from the Security Alliance](https://frameworks.securityalliance.org/opsec/overview/) ::: ## 7. Security Best Practices Checklist While security practices must scale with the hook’s complexity, some fundamentals apply universally. This checklist defines a baseline that all hooks should meet. ### Core Controls - Access control must be minimal and clearly defined with multisig or immutable structures where possible - Reentrancy protection must cover all externally exposed paths - Checks effects interactions should govern all state updates - OpenZeppelin libraries should be used instead of custom foundational code - Gas and balance griefing protections should prevent strategic attacks ### Accounting Safety - Validate correct rounding behavior - Validate deltas and internal balances for every callback - Test internal accounting with invariant checks where appropriate - Avoid reliance on external protocol assumptions by adding guardrails - Handle token return values safely to avoid silent errors - Audit custom math with a specialist and include intermediate step validation tests - Track state growth to avoid unbounded dynamic state - Invariant & Integration Testing use invariant and stateful fuzz testing (e.g., with Foundry or Echidna) to check properties such as delta conservation, fee bounds, and balance consistency across full PoolManager flows. ### Upgradeability Safety - Proxy usage must be documented clearly and tested thoroughly - Upgrades should be time-locked and externally reviewable - Upgrade governance should use multisig - Storage layout must be reviewed for collisions - Upgrade policy must be public and precise ### Transparency Requirements - Publish all audit reports with clear versioning - Maintain a transparent changelog of all upgrades and configuration changes - Provide an active disclosure contact for vulnerability reporting ### General Solidity Best Practices Resources - [Alchemy](https://www.alchemy.com/overviews/smart-contract-security-best-practices) - [Nethermind](https://www.nethermind.io/blog/best-practices-for-writing-secure-smart-contract-code) - [Official Solidity docs on Security Considerations](https://docs.soliditylang.org/en/latest/security-considerations.html) ## 8. Risk Calculator [Scoring Sheet](https://docs.google.com/spreadsheets/d/1oZdKZh13UbqVp3HujAcv-2NKP-j7NQAELhB7kyWlLRE/edit?usp=sharing) A [Uniswap Hooks Security Worksheet](https://docs.google.com/spreadsheets/d/1oZdKZh13UbqVp3HujAcv-2NKP-j7NQAELhB7kyWlLRE/edit?usp=sharing) is provided that implements: - The nine dimension scoring model - All feature flags - Automatic tier calculation - Automatic base recommendation - Dynamic recommendations triggered by feature flags The calculator ensures consistent scoring across teams and audit providers. :::info UF does not review or validate spreadsheet outputs. ::: ## 9. How To Use This Framework - Review the risks associated with hooks - Self score your hook across the nine dimensions - Review your risk tier and feature specific recommendations - Understand your testing, audit depth, bug bounty and monitoring needs - Publish audits, disclosures, and changelogs - Reassess risk periodically as TVL grows or new features are added :::note Teams should re evaluate their tier after any major upgrade or when incentivized liquidity is introduced. ::: ## 10. Future Extensions Community Driven Future community improvements may include: - Additional risk dimensions - Standardized reference hooks - Shared post-mortems - Community driven scoring improvements - Open source testing patterns - Example design patterns and anti patterns The security landscape will evolve and this framework should evolve with it. ## 11. Security Resources Teams developing hooks may find the following providers and tools helpful when implementing the security recommendations in this framework. Some services may offer discounts to hook teams. Inquire directly with the providers for more information. ### Hook Libraries - [OpenZeppelin](https://github.com/OpenZeppelin/uniswap-hooks/tree/master): Solidity library for secure and modular Uniswap hooks - [OpenZeppelin](https://wizard.openzeppelin.com/uniswap-hooks): Uniswap Hooks contract wizard ### Monitoring Services - [Hypernative](https://www.hypernative.io/): real-time anomaly detection, protocol-level monitoring - [Hexagate](https://www.chainalysis.com/product/hexagate/): smart contract threat intelligence and exploit detection ### Formal Verification - [Certora](https://www.certora.com/): rule-based invariant verification for complex math and accounting - [Halmos (by Nethermind)](https://github.com/a16z/halmos): symbolic execution and model checking - [Solidity SMTChecker](https://docs.soliditylang.org/en/latest/smtchecker.html): built-in invariant detection for small properties ### Audits & Security Reviews - [Areta](https://areta.market/): marketplace connecting teams with specialized auditors - [Spearbit](https://cantina.xyz/solutions/spearbit): expert auditors experienced with AMMs and hook systems - [Code4rena](https://code4rena.com/) / [Cantina](https://cantina.xyz/welcome): competitive audit ecosystems - [OpenZeppelin](https://www.openzeppelin.com/security-audits): battle-tested security assessments ### Testing Tools - [Foundry](https://getfoundry.sh/introduction/overview): fuzzing, invariant testing, stateful testing - [Echidna](https://github.com/crytic/echidna): property-based fuzzing - [Diligence Scribble](https://diligence.security/scribble/): annotation-based runtime verification - [Hacken](https://github.com/hknio/uni-v4-hooks-checker): Uniswap v4 Hook Testing Framework ### Operational Security - [Security Alliance OPSEC Framework](https://frameworks.securityalliance.org/opsec/overview) - Key management providers: [Safe (multisig)](https://app.safe.global/welcome), [Fireblocks](https://www.fireblocks.com/) :::note These resources are illustrative, not exhaustive. Teams should choose tools appropriate for their design, risk level, and operational maturity. ::: ## 12. Conclusion This self-assessment guide provides a robust framework for teams to assess their hook security needs. It provides a structured language for understanding risk and a consistent rubric for selecting minimum security measures. The Uniswap Foundation offers this material as a public good but does not perform reviews or certify results. Each team is responsible for using the framework and maintaining a strong security posture. Hooks expand what is possible in automated markets. Strong security practices ensure these innovations remain safe, reliable, and beneficial for the entire ecosystem. --- ## Overview(Llms) # LLMs and AI Integration Large Language Models (LLMs) and AI tools can help developers better understand and work with the Uniswap Protocol. This section provides resources and guidance for leveraging AI assistance when building on Uniswap. ## AI-Powered Documentation The Uniswap documentation is designed to work seamlessly with modern AI tools to provide enhanced developer support and assistance. ### Getting AI Help Use the built-in AI integration features throughout the documentation: - **Copy to AI**: Use the dropdown button on any documentation page to quickly get AI assistance - **Context-Aware**: AI tools receive relevant documentation context for better responses - **Multiple Platforms**: Integrate with popular AI assistants like Claude and ChatGPT ## LLMs.txt This documentation includes an [LLMs.txt file](/llms.txt) that provides comprehensive context about Uniswap for AI systems. This file helps ensure AI assistants can provide accurate and up-to-date information about: - Protocol architecture and concepts - Smart contract interfaces and functionality - SDK usage and integration patterns - Development best practices ## Best Practices for AI-Assisted Development When using AI tools for Uniswap development: 1. **Provide Context**: Always include relevant protocol version (v2, v3, v4) in your queries 2. **Verify Code**: Always test and verify AI-generated code before deployment 3. **Reference Documentation**: Cross-check AI responses against official documentation 4. **Security First**: Have AI-generated smart contracts audited before production use ## Supported AI Platforms The documentation provides optimized integration with: - **Claude**: Advanced reasoning for complex DeFi concepts - **ChatGPT**: Code generation and debugging assistance - **Other LLMs**: Compatible with any AI tool that supports context injection ## Contributing AI Resources Help improve AI assistance for the Uniswap community by: - Reporting AI-generated errors or inaccuracies - Suggesting improvements to the LLMs.txt context file - Contributing examples of effective AI prompts for Uniswap development --- ## Create Pool(Advanced) ## Introduction In this example we will use **ethers.js** and the **Uniswap v4 SDK** to create pools on Uniswap v4. Uniswap v4 is a popular destination for creating markets due to its: - Proven track record and battle-tested codebase - Concentrated liquidity, unlocks capital efficiency - Flexible 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) For this guide, the following Uniswap packages are used: - [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk) - [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core) ## Configuration To initialize a Uniswap v4 Pool _without initial liquidity_, developers should call [`PoolManager.initialize()`](/contracts/v4/concepts/PoolManager) 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. ### Configure the Pool We will first create an example configuration `CurrentConfig` in `config.ts`. It has the format: ```typescript export const CurrentConfig: ExampleConfig = { env: Environment.MAINNET, rpc: { local: 'http://localhost:8545', mainnet: 'https://mainnet.infura.io/v3/YOUR_API_KEY', }, ... poolKey: { currency0: currency0, currency1: currency1, fee: lpFee, tickSpacing: tickSpacing, hooks: HOOK_CONTRACT_ADDRESS, }, } ``` > For native token pairs (Ether), use `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. ## Call `initialize` of Pool Manager contract Now to initialize the `Pool` we need to call the `initialize` function of the Pool Manager Contract. To construct the Pool Manager Contract we need to provide the address of the contract, its ABI and a provider connected to an RPC endpoint. ```typescript const POOL_MANAGER_ADDRESS = '0x000000000004444c5dc75cB358380D2e3dE08A90' // Replace with actual StateView contract address const POOL_MANAGER_ABI = [...]; // Import or define the ABI for PoolManager contract const provider = getProvider() // Provide the right RPC address for the chain const signer = new ethers.Wallet(PRIVATE_KEY, provider) const poolManager = new ethers.Contract( POOL_MANAGER_ADDRESS, POOL_MANAGER_ABI, signer ) ``` We get the `POOL_MANAGER_ADDRESS` for our chain from [Uniswap Deployments](/contracts/v4/deployments). Pools are initialized with a starting price ```typescript const result = await poolManager.initialize( CurrentConfig.poolKey, 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 Now the pool is initialized and you can add liquidity to it. ## Important Note on Initial Liquidity When creating a new pool, it's critical to understand that initializing a pool without liquidity can be dangerous. An empty pool's spot price is freely manipulatable since there is no liquidity to resist price movements. This means that on the first liquidity provision, if proper slippage parameters are not set: 1. Malicious actors can manipulate the price before the first position is minted 2. The first position can be mispriced and have incorrect asset ratios To safely add the first liquidity to a new pool: - Always use appropriate slippage parameters when minting the first position - Consider adding liquidity immediately after pool creation in the same transaction. Reference our [Mint Position guide](/sdk/v4/guides/liquidity/position-minting) for proper liquidity addition practices. --- ## Fetching Pool Data ## Introduction In this example we will use **ethers JS** and **ethers-multicall** to construct a `Pool` object that we can use in the following guides. This guide will **cover**: 1. Computing the PoolId out of PoolKey 2. Referencing the StateView contract and fetching metadata 3. Fetching the positions of all initialized Ticks with multicall 4. Fetching all ticks by their indices with a multicall 5. Constructing the Pool object At the end of the guide, we will have created a `Pool` Object that accurately represents the state of a v4 pool at the time we fetched it. For this guide, the following Uniswap packages are used: - [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk) - [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core) We will also use the `ethers-multicall` npm package: - [`ethers-multicall`](https://www.npmjs.com/package/ethers-multicall) ## Configuration We will first create an example configuration `CurrentConfig` in `config.ts`. It has the format: ```typescript export const CurrentConfig: ExampleConfig = { env: Environment.MAINNET, rpc: { local: 'http://localhost:8545', mainnet: 'https://mainnet.infura.io/v3/YOUR_API_KEY', }, ... poolKey: { currency0: USDC_TOKEN.address, currency1: ETH_TOKEN.address, fee: FEE_AMOUNT_LOW, tickSpacing: TICK_SPACING_TEN, hooks: EMPTY_HOOK, }, } ``` The pool used is defined by a pair of tokens in `constants.ts`. You can also change these two tokens and the other pool parameters in the config, just make sure a pool actually exists for your configuration. Check out the top pools on [Uniswap](https://app.uniswap.org/explore/pools). ```typescript export const ETH_TOKEN = new Token( SupportedChainId.MAINNET, '0x0000000000000000000000000000000000000000', 18, 'ETH', 'Ether' ) export const USDC_TOKEN = new Token( SupportedChainId.MAINNET, '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 6, 'USDC', 'USDC' ) ``` ## Computing the PoolId out of PoolKey In this example, we will construct the **USDC - ETH** Pool with **LOW** fees and without hooks. The SDK provides a method to compute the `PoolId` for this pool: ```typescript const {currency0, currency1, fee, tickSpacing, hooks} = CurrentConfig.poolKey; const poolId = Pool.getPoolId(currency0, currency1, fee, tickSpacing, hooks); ``` ## Referencing the StateView contract and fetching metadata Now that we have the `PoolId` of a **USDC - ETH** Pool, we need to call [StateView](/contracts/v4/guides/state-view) contract to get the pool state. In v4 you need to use `StateLibrary` to read pool state, but offchain systems—such as frontends or analytics services—require a deployed contract with view functions. This is where `StateView` comes in. To construct the Contract we need to provide the address of the contract, its ABI and a provider connected to an RPC endpoint. ```typescript const STATE_VIEW_ADDRESS = '0x7ffe42c4a5deea5b0fec41c94c136cf115597227'; // Replace with actual StateView contract address const STATE_VIEW_ABI = [...]; // Import or define the ABI for StateView contract const provider = getProvider() // Provide the right RPC address for the chain const stateViewContract = new ethers.Contract( STATE_VIEW_ADDRESS, STATE_VIEW_ABI, provider ) ``` We get the `STATE_VIEW_ADDRESS` for our chain from [Uniswap Deployments](/contracts/v4/deployments). Once we have set up our reference to the contract, we can proceed to access its methods. To construct our offchain representation of the Pool, we need to fetch its liquidity, sqrtPrice, currently active tick and the full Tick data. We get the **liquidity**, **sqrtPrice** and **tick** directly from the blockchain by calling `getLiquidity()`and `getSlot0()` on the StateView contract: ```typescript const [slot0, liquidity] = await Promise.all([ stateViewContract.getSlot0(poolId, { blockTag: blockNum, }), stateViewContract.getLiquidity(poolId, { blockTag: blockNum, }), ]) ``` The [getSlot0 function](/contracts/v4/guides/state-view#getting-pool-state) represents the first (0th) storage slot of the pool and exposes multiple useful values in a single function: - `sqrtPriceX96`: The current pool price in Q64.96 fixed-point format. - `tick`: The current tick in which the pool is operating. - `protocolFee` and `lpFee`: Fee parameters for protocol and LP fee tiers. For our use case, we only need the `sqrtPriceX96` and the currently active `tick`. ## Fetching all Ticks v4 pools use ticks to [concentrate liquidity](/concepts/protocol/concentrated-liquidity) in price ranges and allow for better pricing of trades. Even though most Pools only have a couple of **initialized ticks**, it is possible that a pools liquidity is defined by thousands of **initialized ticks**. In that case, it can be very expensive or slow to get all of them with normal RPC calls. If you are not familiar with the concept of ticks, check out the [`introduction`](/concepts/protocol/concentrated-liquidity#ticks). To access tick data, we will use the `getTickInfo` function of the State View contract: ```solidity function getTickInfo(PoolId poolId, int24 tick) external view returns ( uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128 ) ``` The `tick` parameter that we provide the function with is the **index** (memory position) of the Tick we are trying to fetch. To get the indices of all initialized Ticks of the Pool, we can calculate them from the **tickBitmaps**. To fetch a `tickBitmap` we use a `getTickBitmap` function of the State View contract: ```solidity function getTickBitmap( PoolId poolId, int16 wordPosition ) external view returns (uint256 tickBitmap) ``` A pool stores lots of bitmaps, each of which contain the status of 256 Ticks. The parameter `int16 wordPosition` the function accepts is the position of the bitMap we want to fetch. We can calculate all the position of bitMaps (or words as they are sometimes called) from the `tickSpacing` of the Pool, which is in turn dependant on the Fee tier. So to summarise we need 4 steps to fetch all initialized ticks: 1. Calculate all bitMap positions from the tickSpacing of the Pool. 2. Fetch all bitMaps using their positions. 3. Calculate the memory positions of all Ticks from the bitMaps. 4. Fetch all Ticks by their memory position. We will use multicalls for the fetch calls. ## Multicall Multicall contracts **aggregate results** from multiple contract calls and therefore allow sending multiple contract calls in **one RPC request**. This can improve the **speed** of fetching large amounts of data significantly and ensures that the data fetched is all from the **same block**. We will use the Multicall2 contract by MakerDAO. We use the `ethers-muticall` npm package to easily interact with the Contract. ## Calculating all bitMap positions As mentioned, Uniswap v4 Pools store **bitmaps**, also called *words*, that represent the state of **256 initializable ticks** at a time. The value at a bit of a word is 1 if the tick at this index is initialized and 0 if it isn't. We can calculate the positions of initialized ticks from the **words** of the Pool. All ticks of Uniswap v4 pools are between the indices `-887272` and `887272`. We can calculate the minimum and maximum word from these indices and the Pool's tickSpacing: ```typescript function tickToWord(tick: number): number { let compressed = Math.floor(tick / tickSpacing) if (tick < 0 && tick % tickSpacing !== 0) { compressed -= 1 } return compressed >> 8 } const minWord = tickToWord(-887272) const maxWord = tickToWord(887272) ``` Ticks can only be initialized at indices that are **divisible by the tickSpacing**. One word contains 256 ticks, so we can compress the ticks by right shifting 8 bit. ## Fetching bitMaps from their position Knowing the positions of words, we can now fetch them using multicall. First we initialize our multicall providers and State View Contract: ```typescript const ethersProvider = new ethers.providers.JsonRpcProvider("YOUR_RPC_URL") const multicallProvider = new Provider(ethersProvider) await multicallProvider.init() const stateViewContract = new Contract(STATE_VIEW_ADDRESS, STATE_VIEW_ABI) ``` The `multicallProvider` creates the multicall request and sends it via the ethers Provider. Next we loop through all possible word positions and add a `getTickBitmap` call for each: ```typescript let calls: any[] = [] let wordPosIndices: number[] = [] for (let i = minWord; i <= maxWord; i++) { wordPosIndices.push(i) calls.push(stateViewContract.getTickBitmap(poolId, i)) } ``` We also keep track of the word position indices to be able to loop through them in the same order we added the calls to the array. We use the `multicallProvider.all()` function to send a multicall and map the results: ```typescript const results: bigint[] = (await multicallProvider.all(calls)).map( (ethersResponse) => { return BigInt(ethersResponse.toString()) } ) ``` A great visualization of what the bitMaps look like can be found in the [Uniswap v3 development book](https://uniswapv3book.com/docs/milestone_2/tick-bitmap-index/](https://uniswapv3book.com/milestone_2/tick-bitmap-index.html): We encourage anyone trying to get a deeper understanding of the Uniswap protocol to read the Book. ## Calculating the memory positions of all Ticks Now that we fetched all **bitMaps**, we check which ticks are initialized and calculate the **tick position** from the **word index** and the **tickSpacing** of the pool. We check if a tick is **initialized** inside the word by shifting a bit by the index we are looking at and performing a bitwise AND operation: ```typescript const bit = 1n const initialized = (bitmap & (bit << BigInt(i))) !== 0n ``` If the tick is **initialized**, we revert the compression from tick to word we made earlier by multiplying the word index with 256, which is the same as left shifting by 8 bit, adding the position we are currently at, and multiplying with the tickSpacing: ```typescript const tickIndex = (ind * 256 + i) * tickSpacing ``` The whole loop looks like this: ```typescript const tickIndices: number[] = [] for (let j = 0; j < wordPosIndices.length; j++) { const ind = wordPosIndices[j] const bitmap = results[j] if (bitmap !== 0n) { for (let i = 0; i < 256; i++) { const bit = 1n const initialized = (bitmap & (bit << BigInt(i))) !== 0n if (initialized) { const tickIndex = (ind * 256 + i) * tickSpacing tickIndices.push(tickIndex) } } } } ``` We now have an array containing the indices of all initialized Ticks. ## Fetching all Ticks by their indices We use the multicallProvider again to execute an aggregated read call for all tick indices. We create an array of call Promises again and use `.all()` to make our multicall: ```typescript const calls: any[] = [] for (const index of tickIndices) { calls.push(stateViewContract.getTickInfo(poolId, index)) } const results = await multicallProvider.all(calls) ``` Again, the order of the results array is the same as the elements in **tickIndices**. We are able to combine the **tickIndices** and **results** array to create an array of `Tick` objects: ```typescript const allTicks: Tick[] = [] for (let i = 0; i < tickIndices.length; i++) { const index = tickIndices[i] const ethersResponse = results[i] const tick = new Tick({ index, liquidityGross: JSBI.BigInt(ethersResponse.liquidityGross.toString()), liquidityNet: JSBI.BigInt(ethersResponse.liquidityNet.toString()), }) allTicks.push(tick) } ``` We need to parse the response from our RPC provider to JSBI values that the v4-sdk can work with. ## Constructing the Pool We have everything to construct our `Pool` now: ```typescript const usdcWethPool = new Pool( USDC, WETH, feeAmount, slot0.sqrtPriceX96, liquidity, slot0.tick, allTicks ) ``` With this fully initialized Pool, we can make accurate offchain calculations. --- ## Adding and Removing Liquidity ## Introduction This guide will cover: 1. **Setting up liquidity operations** – Preparing to add/remove liquidity from v4 positions, including fetching position details, handling native ETH vs ERC20 tokens, and configuring Permit2 for ERC20 token approvals. 2. **Adding liquidity to existing positions** – Using the v4 SDK to increase liquidity with `addCallParameters`, handling native ETH positions, and executing transactions via PositionManager multicall. 3. **Removing liquidity from positions** – Using `removeCallParameters` to decrease or fully exit positions, handling proportional withdrawals, and token collection. For this guide, the following Uniswap packages are used: - [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk) - [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core) ## v4 Architecture and Key Changes ### Native ETH Handling Unlike v3, Uniswap v4 has native support for ETH without wrapping to WETH. This requires special handling in the SDK: ```typescript // ✅ Correct: Using Ether.onChain() for native ETH const token0 = Ether.onChain(chainId) ``` ### Position Manager Multicall All v4 position operations use the `PositionManager` contract's `multicall` function with encoded action sequences: ```typescript const { calldata, value } = V4PositionManager.addCallParameters(position, options) await walletClient.writeContract({ address: POSITION_MANAGER_ADDRESS, functionName: 'multicall', args: [[calldata]], value: BigInt(value), }) ``` ## Adding Liquidity to Existing Positions ### Theory: IncreaseLiquidityOptions When adding liquidity to existing positions, we use `IncreaseLiquidityOptions` which combines: - `CommonOptions`: slippage, deadline, hookData - `ModifyPositionSpecificOptions`: tokenId - `CommonAddLiquidityOptions`: useNative, batchPermit ### Step 1: Fetch Position Details ```typescript interface PositionDetails { tokenId: bigint tickLower: number tickUpper: number liquidity: bigint poolKey: { currency0: Address currency1: Address fee: number tickSpacing: number hooks: Address } token0: Currency // Can be Ether or Token token1: Token // Always Token in current implementation currentTick: number sqrtPriceX96: string poolLiquidity: string } async function getPositionDetails(tokenId: bigint): Promise { // Fetch position info from PositionManager const [poolKey, infoValue] = await publicClient.readContract({ address: POSITION_MANAGER_ADDRESS, abi: POSITION_MANAGER_ABI, functionName: 'getPoolAndPositionInfo', args: [tokenId], }) // Create proper Currency instances let token0: Currency if (isNativeETH(poolKey.currency0)) { token0 = Ether.onChain(chainId) } else { const decimals0 = await fetchTokenDecimals(poolKey.currency0) const symbol0 = await getTokenSymbol(poolKey.currency0) token0 = new Token(chainId, poolKey.currency0, decimals0, symbol0) } const token1 = new Token(chainId, poolKey.currency1, decimals1, symbol1) return { tokenId, tickLower: infoValue.tickLower, tickUpper: infoValue.tickUpper, liquidity: infoValue.liquidity, poolKey, token0, token1, // ... other fields } } ``` ### Step 2: Configure Permit2 (Recommended) ```typescript const PERMIT2_TYPES = { PermitDetails: [ { name: 'token', type: 'address' }, { name: 'amount', type: 'uint160' }, { name: 'expiration', type: 'uint48' }, { name: 'nonce', type: 'uint48' }, ], PermitBatch: [ { name: 'details', type: 'PermitDetails[]' }, { name: 'spender', type: 'address' }, { name: 'sigDeadline', type: 'uint256' }, ], } async function configurePermit2(positionDetails: EnhancedPositionDetails, deadline: number) { const permitDetails = [] // Add token1 (always ERC20) const [, , nonce1] = await publicClient.readContract({ address: PERMIT2_ADDRESS, abi: PERMIT2_ABI, functionName: 'allowance', args: [userAddress, positionDetails.token1.address, POSITION_MANAGER_ADDRESS], }) permitDetails.push({ token: positionDetails.token1.address, amount: (2n ** 160n - 1n).toString(), expiration: deadline.toString(), nonce: nonce1.toString(), }) // Add token0 only if it's not native ETH if (!positionDetails.token0.isNative) { const [, , nonce0] = await publicClient.readContract({ address: PERMIT2_ADDRESS, abi: PERMIT2_ABI, functionName: 'allowance', args: [userAddress, (positionDetails.token0 as Token).address, POSITION_MANAGER_ADDRESS], }) permitDetails.push({ token: (positionDetails.token0 as Token).address, amount: (2n ** 160n - 1n).toString(), expiration: deadline.toString(), nonce: nonce0.toString(), }) } const permitData = { details: permitDetails, spender: POSITION_MANAGER_ADDRESS, sigDeadline: deadline.toString(), } // Sign Permit2 data const signature = await walletClient.signTypedData({ account, domain: { name: 'Permit2', chainId, verifyingContract: PERMIT2_ADDRESS, }, types: PERMIT2_TYPES, primaryType: 'PermitBatch', message: permitData, }) return { owner: userAddress, permitBatch: permitData, signature, } } ``` ### Step 3: Create Position and Add Liquidity ```typescript async function addLiquidityToPosition( positionDetails: EnhancedPositionDetails, amount0: string, amount1: string, slippageTolerance: number = 0.05 ) { // Create Pool instance const pool = new Pool( positionDetails.token0, positionDetails.token1, positionDetails.poolKey.fee, positionDetails.poolKey.tickSpacing, positionDetails.poolKey.hooks, positionDetails.sqrtPriceX96, positionDetails.poolLiquidity, positionDetails.currentTick ) // Create currency amounts const amount0Currency = CurrencyAmount.fromRawAmount(positionDetails.token0, amount0) const amount1Currency = CurrencyAmount.fromRawAmount(positionDetails.token1, amount1) // Create Position from amounts const position = Position.fromAmounts({ pool, tickLower: positionDetails.tickLower, tickUpper: positionDetails.tickUpper, amount0: amount0Currency.quotient, amount1: amount1Currency.quotient, useFullPrecision: true, }) // Configure options const slippagePct = new Percent(Math.floor(slippageTolerance * 100), 10_000) const deadline = Math.floor(Date.now() / 1000) + 1200 // 20 minutes const addOptions: AddLiquidityOptions = { // CommonOptions slippageTolerance: slippagePct, deadline: deadline.toString(), hookData: '0x', // ModifyPositionSpecificOptions tokenId: positionDetails.tokenId.toString(), // CommonAddLiquidityOptions ...(positionDetails.token0.isNative && { useNative: Ether.onChain(chainId) }), batchPermit: await configurePermit2(positionDetails, deadline), } // Generate calldata and execute const { calldata, value } = V4PositionManager.addCallParameters(position, addOptions) const txHash = await walletClient.writeContract({ account, address: POSITION_MANAGER_ADDRESS, chain: unichain, abi: POSITION_MANAGER_ABI, functionName: 'multicall', args: [[calldata]], value: BigInt(value.toString()), }) return { txHash, addedAmounts: { amount0, amount1 } } } ``` ## Removing Liquidity from Positions ### Theory: RemoveLiquidityOptions When removing liquidity, we use `RemoveLiquidityOptions` which includes: - `CommonOptions`: slippage, deadline, hookData - `ModifyPositionSpecificOptions`: tokenId - `RemoveLiquiditySpecificOptions`: liquidityPercentage, burnToken, permit ### Step 1: Calculate Liquidity to Remove ```typescript function calculateLiquidityToRemove( currentLiquidity: bigint, percentageToRemove: number // 0.25 = 25%, 1.0 = 100% ): { liquidityToRemove: bigint liquidityPercentage: Percent } { const liquidityToRemove = (currentLiquidity * BigInt(Math.floor(percentageToRemove * 10000))) / 10000n const liquidityPercentage = new Percent(Math.floor(percentageToRemove * 100), 100) return { liquidityToRemove, liquidityPercentage } } ``` ### Step 2: Remove Liquidity Implementation ```typescript async function removeLiquidityFromPosition( positionDetails: EnhancedPositionDetails, percentageToRemove: number, // 0.25 = 25%, 1.0 = 100% slippageTolerance: number = 0.05, burnTokenIfEmpty: boolean = false ) { const { liquidityToRemove, liquidityPercentage } = calculateLiquidityToRemove( positionDetails.liquidity, percentageToRemove ) // Create Pool instance const pool = new Pool( positionDetails.token0, positionDetails.token1, positionDetails.poolKey.fee, positionDetails.poolKey.tickSpacing, positionDetails.poolKey.hooks, positionDetails.sqrtPriceX96, positionDetails.poolLiquidity, positionDetails.currentTick ) // Create Position instance with current liquidity const position = new Position({ pool, tickLower: positionDetails.tickLower, tickUpper: positionDetails.tickUpper, liquidity: positionDetails.liquidity.toString(), }) // Configure remove options const slippagePct = new Percent(Math.floor(slippageTolerance * 100), 10_000) const deadline = Math.floor(Date.now() / 1000) + 1200 const removeOptions: RemoveLiquidityOptions = { // CommonOptions slippageTolerance: slippagePct, deadline: deadline.toString(), hookData: '0x', // ModifyPositionSpecificOptions tokenId: positionDetails.tokenId.toString(), // RemoveLiquiditySpecificOptions liquidityPercentage, burnToken: burnTokenIfEmpty && percentageToRemove === 1.0, // permit: optional NFT permit if transaction sender doesn't own the NFT } // Generate calldata and execute const { calldata, value } = V4PositionManager.removeCallParameters(position, removeOptions) const txHash = await walletClient.writeContract({ account, address: POSITION_MANAGER_ADDRESS, chain: unichain, abi: POSITION_MANAGER_ABI, functionName: 'multicall', args: [[calldata]], value: BigInt(value.toString()), }) return { txHash, removedLiquidity: liquidityToRemove, percentageRemoved: percentageToRemove, tokenBurned: burnTokenIfEmpty && percentageToRemove === 1.0, } } ``` ## Complete Example: Add/Remove Workflow ```typescript async function completeAddRemoveWorkflow() { const tokenId = 123456n // 1. Fetch position details const positionDetails = await getPositionDetails(tokenId) console.log(`Position: ${positionDetails.token0.symbol}/${positionDetails.token1.symbol}`) // 2. Add liquidity const addResult = await addLiquidityToPosition( positionDetails, '1000000000000000', // 0.001 ETH '1000000', // 1 USDC 0.05 // 5% slippage ) console.log(`Added liquidity: ${addResult.txHash}`) // 3. Wait and verify await new Promise((resolve) => setTimeout(resolve, 5000)) const updatedPosition = await getPositionDetails(tokenId) // 4. Remove 50% of liquidity const removeResult = await removeLiquidityFromPosition( updatedPosition, 0.5, // 50% 0.05, // 5% slippage false // don't burn token ) console.log(`Removed 50% liquidity: ${removeResult.txHash}`) return { addResult, removeResult } } ``` --- ## Collecting Fee ## Introduction This guide will cover: 1. **Setting up our fee collection** – Preparing to collect fees from a v4 position, including fetching position details, computing the `poolId`, using `StateView` to read fee growth data, and calculating the unclaimed fees off-chain. 2. **Submitting our fee collection transaction** – Using the v4 SDK to create the transaction calldata (with `collectCallParameters`), executing the call (via a multicall on the PositionManager). For this guide, the following Uniswap packages are used: - [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk) - [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core) ## Fee Calculation Theory In Uniswap v4, fees are not stored directly. Instead, fees must be calculated using **differential calculation** from cumulative values called `feeGrowthInside`. ### feeGrowthInside Concept ```text feeGrowthInside = Cumulative fees generated in pool ÷ Active liquidity at that time ``` ### Unclaimed Fees (Currently Collectible Fees) ```typescript const Q128 = 2n ** 128n unclaimedFees = ((feeGrowthCurrent - feeGrowthLast) * liquidity) / Q128 ``` **Compute unclaimed fees off-chain using the v4 formula:** - `feeGrowthInsideCurrentX128` (for token0 and token1): the total fee growth inside the range as of now. - `feeGrowthInsideLastX128` (for token0 and token1): the fee growth inside the range at the last time the position's state was updated (recorded in the position info). - `liquidity`: the amount of liquidity in the position. **Implementation**: ```typescript function calculateUnclaimedFeesV4( liquidity: bigint, feeGrowthInside0Current: bigint, feeGrowthInside1Current: bigint, feeGrowthInside0Last: bigint, feeGrowthInside1Last: bigint ): UnclaimedFees { const Q128 = 2n ** 128n // Overflow protection: return 0 if current is less than last const feeGrowthDelta0 = feeGrowthInside0Current >= feeGrowthInside0Last ? feeGrowthInside0Current - feeGrowthInside0Last : 0n const feeGrowthDelta1 = feeGrowthInside1Current >= feeGrowthInside1Last ? feeGrowthInside1Current - feeGrowthInside1Last : 0n return { token0Fees: (feeGrowthDelta0 * liquidity) / Q128, token1Fees: (feeGrowthDelta1 * liquidity) / Q128, } } ``` ### Lifetime Fees (Total Fees Since Position Creation) ```typescript lifetimeFees = (feeGrowthCurrent * liquidity) / Q128 ``` **Implementation**: ```typescript function calculateLifetimeFeesV4( liquidity: bigint, feeGrowthInside0Current: bigint, feeGrowthInside1Current: bigint ): LifetimeFees { const Q128 = 2n ** 128n return { token0LifetimeFees: (feeGrowthInside0Current * liquidity) / Q128, token1LifetimeFees: (feeGrowthInside1Current * liquidity) / Q128, } } ``` ### 3. Collected Fees Estimate **Calculation basis**: ```text Total fees = Collected + Unclaimed ∴ Collected = Total fees - Unclaimed ``` ## v4 Architecture and Required Changes ### Fee Accrual and Credit Changes **Fee Accrual and Credit:** Uniswap v4 changes how fee accrual is handled when modifying liquidity. In v3, adding or removing liquidity didn't automatically claim fees – you had to call a separate `collect` function to pull out accrued fees. In v4, **accrued fees act like a credit** that is automatically applied or required depending on liquidity changes. Increasing a position's liquidity will **roll any unclaimed fees into the position's liquidity**, and decreasing liquidity will **automatically withdraw** the proportional unclaimed fees for that position. This means that partially removing liquidity in v4 will force-claim the fees earned by that liquidity portion. However, if you want to claim fees without changing liquidity, you can perform a liquidity change of zero (as we'll do in this guide). ### Why StateView is Required In v4, all pools are managed by a single `PoolManager`, so direct access to pool contracts is not possible. Instead, data must be read through the `StateView` contract. ```typescript // v4 approach (required) await StateView.getPositionInfo(poolId, owner, tickLower, tickUpper, salt) ``` ### salt In v4, the same owner can have multiple positions in the same tick range. `salt` is used to identify them individually. **Derive the salt for the position:** As noted, v4 positions include a `salt` to distinguish positions with identical range by the same owner. For positions created via the `PositionManager` (which holds ownership in the pool), the salt **is the NFT token ID, encoded as a 32-byte value**. ```typescript // Use tokenId as salt (PositionManager standard) const salt = `0x${tokenId.toString(16).padStart(64, '0')}` ``` ## Code Implementation Flow ### Phase 1: Position Information Retrieval ### Step 1: Position List Retrieval Retrieves the tokenIds for v4 positions owned by a specific address from a Subgraph. ### Step 2: Position Details Retrieval ```typescript async function getPositionDetails(tokenId: bigint): Promise { const [poolKey, infoValue] = await publicClient.readContract({ address: POSITION_MANAGER_ADDRESS, functionName: 'getPoolAndPositionInfo', args: [tokenId], }) // poolId calculation const poolId = Pool.getPoolId(currency0, currency1, poolKey.fee, poolKey.tickSpacing, poolKey.hooks) } ``` ### Step 3: Stored Fee State Retrieval ```typescript async function getStoredPositionInfoV4(positionDetails, tokenId, owner) { const salt = `0x${tokenId.toString(16).padStart(64, '0')}` const [liquidity, feeGrowthInside0Last, feeGrowthInside1Last] = await publicClient.readContract({ address: STATE_VIEW_ADDRESS, functionName: 'getPositionInfo', args: [poolId, POSITION_MANAGER_ADDRESS, tickLower, tickUpper, salt], }) } ``` ### Step 4: Current Fee Growth Values Retrieval **Read the current fee growth in the pool for the position's range:** To compute how much fees are unclaimed, we need the **current** fee growth inside the range and compare it to the last snapshot. We could manually fetch global fee growth and subtract out-of-range values, but `StateView` provides a convenience: [`getFeeGrowthInside(poolId, tickLower, tickUpper)`](/contracts/v4/reference/periphery/interfaces/IStateView) will calculate the up-to-date fee growth inside that tick range for each token. This function reads the latest pool state (including global fee growth) and subtracts the parts outside the range. It accounts for any new trades that happened since the last snapshot. ```typescript async function getCurrentFeeGrowthV4(positionDetails) { const [feeGrowthInside0X128, feeGrowthInside1X128] = await publicClient.readContract({ address: STATE_VIEW_ADDRESS, functionName: 'getFeeGrowthInside', args: [poolId, tickLower, tickUpper], }) } ``` ### Phase 2: Submitting Our Fee Collection Transaction Collecting fees in v4 is done via the `PositionManager` contract's `modifyLiquidities` function with a specific sequence of actions. We will use the Uniswap v4 SDK to construct the required calldata and then send the transaction. ### Build the fee-collection calldata with collectCallParameters The Uniswap v4 SDK provides a helper `V4PositionManager.collectCallParameters(...)` that produces the calldata for the necessary multicall to collect fees. Under the hood, this will encode two actions: a `DECREASE_LIQUIDITY` with `liquidity = 0` (and min amounts = 0) and a `TAKE_PAIR` to sweep both tokens to a recipient. We need to supply the SDK with our position details and our desired options. First, create a `Position` object for the position (this requires the pool info and position info we fetched): ```typescript async function collectFeesViaMulticall(tokenId, userAddress) { // Create Position object using pool and position parameters const position = new Position({ pool, tickLower: positionDetails.tickLower, tickUpper: positionDetails.tickUpper, liquidity: positionDetails.liquidity.toString(), }) // Specify collect options const collectOptions = { tokenId: tokenId, recipient: userAddress, slippageTolerance, deadline, hookData, } // Generate command with v4 SDK const { calldata, value } = V4PositionManager.collectCallParameters(position, collectOptions) // Execute with multicall const txHash = await walletClient.writeContract({ account, chain: unichain, address: POSITION_MANAGER_ADDRESS, abi: POSITION_MANAGER_ABI, functionName: 'multicall', args: [[calldata]], value: BigInt(value), }) } ``` Let's break this down: we created a `Position` object using the pool and position parameters. We then specify `collectOptions` including the NFT `tokenId`, a `recipient` address (fees will be sent to this address), and a `deadline`. Because fee collection is not really subject to price slippage, we can set slippage tolerance to 0 and simply expect whatever fees are available. The SDK's `collectCallParameters` returns an object with `calldata` (the encoded bytes to send to the PositionManager) and `value` (the ETH value to send with the transaction, if needed). In our case, `value` will typically be `0` because we are not providing any additional ETH; we are only withdrawing. (The `value` would be non-zero if one of the actions required sending ETH to the contract, e.g. if adding liquidity to an ETH pair.) **Under the hood:** The `calldata` produced encodes exactly two actions in `modifyLiquidities`: `Actions.DECREASE_LIQUIDITY` followed by `Actions.TAKE_PAIR`. The first action includes our `tokenId` and zeros for liquidity and min amounts, and the second action includes the two token currencies and the recipient address. Using a zero liquidity decrease is a trick to trigger the pool to calculate fees owed without actually changing the liquidity. The `TAKE_PAIR` then instructs the contract to transfer both token0 and token1 fee amounts out to us. (If our pool involved native ETH, one of the `Currency` entries in this param will be `Currency.wrap(0)` as shown, which signals the contract to send ETH. No manual WETH unwrap is needed – v4 handles it natively.) ### Phase 3: Verify the Fees Were Collected Once the transaction is mined, you'll want to confirm that the fees made it to the `recipient`. There are a few ways to verify: ### Check the Transaction Receipt Logs For ERC-20 tokens, the fee amounts taken will appear as `Transfer` events from the pool or PositionManager contract to your address. Token contracts will emit these events when the PositionManager transfers the fees to you. You can parse the receipt for `Transfer` logs of `token0` and `token1`. The amounts in those events should match the fees we calculated (or be very close, allowing for rounding). ```typescript async function verifyFeeCollection(receipt, userAddress, positionDetails, ethBalanceBefore) { // Search for ERC-20 Transfer events const transferSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' const erc20Transfers = receipt.logs .filter( (log) => log.topics[0] === transferSignature && log.topics[2]?.toLowerCase().includes(userAddress.slice(2).toLowerCase()) ) .map((log) => ({ token: log.address as Address, amount: BigInt(log.data), })) } ``` ### Check Your Token Balances You can simply measuring the balance change in your wallet before vs. after the call. For example, read your token balances (and ETH balance) prior to calling, then after the transaction confirm the increases. Because v4 might auto-wrap or unwrap ETH, if one of the tokens was ETH you should check your ETH balance difference. In ETH pools, no ERC-20 transfer event will fire for the ETH – the ETH will be sent directly to you (as an internal transfer), which is why checking the balance or the transaction's internal traces is necessary to confirm the amount. ```typescript // Check native ETH balance changes const hasNativeETH = isNativeETH(positionDetails.poolKey.currency0) if (hasNativeETH) { const ethBalanceAfter = await publicClient.getBalance({ address: userAddress }) const ethChange = ethBalanceAfter - ethBalanceBefore if (ethChange > 0n) { collectedFees.push({ token: '0x0000000000000000000000000000000000000000', amount: ethChange, }) } } ``` --- ## Fetching Positions ## Introduction This guide covers how to fetch and analyze liquidity positions in Uniswap v4 using the v4-sdk. For this guide, the following Uniswap packages are used: - [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk) - [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core) ## Key Differences from v3 The v4 PositionManager does not implement ERC721Enumerable, so `tokenOfOwnerByIndex` is not available. This requires using the subgraph to discover position IDs. Additionally, v4 uses a packed data format for position information. ## Setup ```typescript const POSITION_MANAGER_ADDRESS = '0x4529a01c7a0410167c5740c487a8de60232617bf' //unichain const publicClient = createPublicClient({ chain: unichain, transport: http(), }) ``` ## Fetching Position IDs ```typescript interface SubgraphPosition { id: string tokenId: string owner: string } const GET_POSITIONS_QUERY = ` query GetPositions($owner: String!) { positions(where: { owner: $owner }) { tokenId owner id } } ` const UNICHAIN_SUBGRAPH_URL = 'https://gateway.thegraph.com/api/subgraphs/id/EoCvJ5tyMLMJcTnLQwWpjAtPdn74PcrZgzfcT5bYxNBH' async function getPositionIds(owner: Address): Promise { // You can explore queries at: https://thegraph.com/explorer/subgraphs/EoCvJ5tyMLMJcTnLQwWpjAtPdn74PcrZgzfcT5bYxNBH?view=Query&chain=arbitrum-one const headers = { Authorization: 'Bearer ' + process.env.GRAPH_KEY, // Get your API key from https://thegraph.com/studio/apikeys/ } const response = await request<{ positions: SubgraphPosition[] }>( UNICHAIN_SUBGRAPH_URL, GET_POSITIONS_QUERY, { owner: owner.toLowerCase() }, headers ) return response.positions.map((p) => BigInt(p.tokenId)) } ``` ## Decoding Packed Position Data v4 stores position information in a packed format. Here's how to decode it: ```typescript interface PackedPositionInfo { getTickUpper(): number getTickLower(): number hasSubscriber(): boolean } function decodePositionInfo(value: bigint): PackedPositionInfo { return { getTickUpper: () => { const raw = Number((value >> 32n) & 0xffffffn) return raw >= 0x800000 ? raw - 0x1000000 : raw }, getTickLower: () => { const raw = Number((value >> 8n) & 0xffffffn) return raw >= 0x800000 ? raw - 0x1000000 : raw }, hasSubscriber: () => (value & 0xffn) !== 0n, } } ``` ## Position Details Interface ```typescript interface PositionDetails { tokenId: bigint tickLower: number tickUpper: number liquidity: bigint poolKey: { currency0: Address currency1: Address fee: number tickSpacing: number hooks: Address } } ``` ## Contract ABI ```typescript const POSITION_MANAGER_ABI = [ { name: 'getPoolAndPositionInfo', type: 'function', inputs: [{ name: 'tokenId', type: 'uint256' }], outputs: [ { name: 'poolKey', type: 'tuple', components: [ { name: 'currency0', type: 'address' }, { name: 'currency1', type: 'address' }, { name: 'fee', type: 'uint24' }, { name: 'tickSpacing', type: 'int24' }, { name: 'hooks', type: 'address' }, ], }, { name: 'info', type: 'uint256' }, ], }, { name: 'getPositionLiquidity', type: 'function', inputs: [{ name: 'tokenId', type: 'uint256' }], outputs: [{ name: 'liquidity', type: 'uint128' }], }, ] as const ``` ## Fetching Position Details ```typescript async function getPositionDetails(tokenId: bigint): Promise { // Get pool key and packed position info // Get pool key and packed position info const [poolKey, infoValue] = (await publicClient.readContract({ address: POSITION_MANAGER_ADDRESS, abi: POSITION_MANAGER_ABI, functionName: 'getPoolAndPositionInfo', args: [tokenId], })) as readonly [ { currency0: Address currency1: Address fee: number tickSpacing: number hooks: Address }, bigint ] // Get current liquidity const liquidity = (await publicClient.readContract({ address: POSITION_MANAGER_ADDRESS, abi: POSITION_MANAGER_ABI, functionName: 'getPositionLiquidity', args: [tokenId], })) as bigint // Decode packed position info const positionInfo = decodePositionInfo(infoValue) return { tokenId, tickLower: positionInfo.getTickLower(), tickUpper: positionInfo.getTickUpper(), liquidity, poolKey, } } ``` ## Usage Example ```typescript async function fetchUserPositions(userAddress: Address) { try { // Get position IDs from subgraph const tokenIds = await getPositionIds(userAddress) console.log(`Found ${tokenIds.length} positions on Unichain`) // Fetch details for each position for (const tokenId of tokenIds) { const details = await getPositionDetails(tokenId) console.log(`Position ${tokenId}:`) console.log(` Token0: ${details.poolKey.currency0}`) console.log(` Token1: ${details.poolKey.currency1}`) console.log(` Fee: ${details.poolKey.fee / 10000}%`) console.log(` Range: ${details.tickLower} to ${details.tickUpper}`) console.log(` Liquidity: ${details.liquidity.toString()}`) console.log(` Hooks: ${details.poolKey.hooks}`) console.log('---') } } catch (error) { console.error('Error:', error) } } // Example usage fetchUserPositions('0xYourAddress' as Address) ``` ## Resources - [Uniswap v4 SDK](https://github.com/Uniswap/sdks/tree/main/sdks/v4-sdk) - [Unichain Documentation](https://docs.unichain.org/) --- ## Minting a position ## Introduction This guide will introduce us to liquidity positions in Uniswap v4 and present the v4-sdk classes and contracts used to interact with the protocol. For this guide, the following Uniswap packages are used: - [`@uniswap/v3-sdk`](https://www.npmjs.com/package/@uniswap/v3-sdk) - [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk) - [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core) ## Overview of Uniswap v4 Position Minting Uniswap v4 introduces a new PositionManager contract and a corresponding v4 SDK to manage liquidity positions. Like v3, liquidity positions are represented as NFTs, but v4 uses a command-based interface for bundling actions (e.g., minting liquidity and transferring tokens) into a single transaction. The v4 SDK provides high-level classes – Pool, Position, and v4PositionManager – to help construct these transactions in JavaScript/TypeScript. This guide explains how to create (mint) a new liquidity position using the Uniswap v4 SDK. We will cover: - Setting up a Pool and Position for minting - Configuring MintOptions (all parameters, types, and defaults) - Using v4PositionManager.addCallParameters to get transaction data ## Preparing Pool and Position Objects Before minting, you need a Pool instance reflecting the current on-chain state and a Position defining your desired liquidity parameters: ### Step 1: Define Token Information ```typescript const ETH_NATIVE = Ether.onChain(ChainId.Mainnet) const ETH_TOKEN = new Token( ChainId.MAINNET, '0x0000000000000000000000000000000000000000', 18, 'ETH', 'Ether' ) const USDC_TOKEN = new Token( ChainId.MAINNET, '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 6, 'USDC', 'USDC' ) ``` > **Note**: In v4, pools are identified by a PoolKey (which includes token0, token1, fee, tick spacing, and hook address). The SDK's Pool class helps manage these details. Ensure that the token order (token0 vs token1) and the hook address match the actual pool. ### Step 2: Fetch Pool State Before creating a Pool instance, you need to fetch the current state from the blockchain: ```typescript // Define constants for the function // The STATE_VIEW_ADDRESS should be imported from your constants file // or defined at the top of your file const STATE_VIEW_ADDRESS = '0x86e8631a016f9068c3f085faf484ee3f5fdee8f2'; // Replace with actual StateView contract address const STATE_VIEW_ABI = [...]; // Import or define the ABI for StateView contract const CHAIN_ID = xxx; // Replace Chain id // Create a viem client for reading blockchain data const client = createPublicClient({ chain: CHAIN_ID, transport: http() }); // Define pool parameters const fee = 500; // Fee tier (e.g., 500 = 0.05%) const tickSpacing = 10; // Tick spacing for this fee tier const hookAddress = '0x0000...'; // Hook address, if any (or zero address) // Get the pool ID using SDK helper const poolId = Pool.getPoolId(token0, token1, fee, tickSpacing, hookAddress); // Fetch current pool state from the blockchain const [slot0, liquidity] = await Promise.all([ client.readContract({ address: STATE_VIEW_ADDRESS, abi: STATE_VIEW_ABI, functionName: 'getSlot0', args: [poolId as `0x${string}`], }), client.readContract({ address: STATE_VIEW_ADDRESS, abi: STATE_VIEW_ABI, functionName: 'getLiquidity', args: [poolId as `0x${string}`], }), ]); // Extract relevant data const sqrtPriceX96Current = slot0[0] as bigint; const currentTick = slot0[1] as number; const currentLiquidity = liquidity as bigint; // Create Pool instance with the fetched data const pool = new Pool( token0, token1, fee, tickSpacing, hookAddress, // Pass the hook address from above sqrtPriceX96Current.toString(), // Convert bigint to string for SDK currentLiquidity.toString(), // Convert bigint to string for SDK currentTick, // Current tick from slot0 ); ``` ### Step 3: Define Position Parameters Now define the parameters for your liquidity position: ```typescript // Define position parameters // These typically come from user input in your interface const fullRange = false // Whether to create a full-range position const tickRange = 500 // Tick range around current price (e.g., 5%) const amountA = 1.0 // Amount of token A to deposit const amountB = 1000.0 // Amount of token B to deposit // Calculate tick boundaries based on user preferences let tickLower: number let tickUpper: number if (fullRange) { // For full-range positions, use Uniswap's minimum and maximum allowed ticks const MIN_TICK = -887272 const MAX_TICK = 887272 // Get tick spacing from the pool (already fetched from blockchain) const poolTickSpacing = pool.tickSpacing // Round tickLower up (closer to the center) // The nearestUsableTick ensures the tick is aligned with tick spacing tickLower = nearestUsableTick(MIN_TICK, poolTickSpacing) // Round tickUpper down (closer to the center) tickUpper = nearestUsableTick(MAX_TICK, poolTickSpacing) } else { // Calculate lower and upper ticks, ensuring they align with tick spacing tickLower = nearestUsableTick(currentTick - tickRangeAmount, tickSpacing) tickUpper = nearestUsableTick(currentTick + tickRangeAmount, tickSpacing) } // Convert human-readable amounts to token amounts with proper decimals const amountADesired = BigInt(Math.floor(amountA * 10 ** ETH_TOKEN.decimals)) const amountBDesired = BigInt(Math.floor(amountB * 10 ** USDC_TOKEN.decimals)) // Ensure token amounts are in the correct order (token0, token1) const amount0Desired = token0IsA ? amountADesired.toString() : amountBDesired.toString() const amount1Desired = token0IsA ? amountBDesired.toString() : amountADesired.toString() ``` ### Step 5: Create a Position Use the SDK to create a Position object that represents your liquidity position: ```typescript // Create a position from the desired token amounts // The SDK will calculate the maximum liquidity possible with these amounts const position = Position.fromAmounts({ pool, tickLower, tickUpper, amount0: amount0Desired, amount1: amount1Desired, useFullPrecision: true, // Use full precision for maximum accuracy }) // You can now access useful information from the position: // position.mintAmounts - The actual amounts needed to mint this position // position.amount0 - The amount of token0 in the position // position.amount1 - The amount of token1 in the position // position.liquidity - The liquidity value of the position console.log('Position liquidity:', position.liquidity.toString()) console.log('Token0 amount:', position.amount0.toExact()) console.log('Token1 amount:', position.amount1.toExact()) ``` > **Alternative**: If you have a specific liquidity amount instead of token amounts, you could use: > > ```typescript > const position = new Position({ > pool, > tickLower, > tickUpper, > liquidity: '1000000000000000000', // Example liquidity amount > }) > ``` ## Understanding MintOptions and Its Parameters Once the Position is defined, the next step is to prepare the MintOptions object. In Uniswap v4 SDK, MintOptions is a type alias that combines three sets of options: CommonOptions, CommonAddLiquidityOptions, and MintSpecificOptions. This structure covers generic transaction settings, options common to any "add liquidity" action, and options unique to minting a new position. ### MintOptions Parameters Explained ```typescript // Import necessary types // Example code showing how to set up MintOptions // These parameters typically come from user input or application state // 1. slippageTolerance (required): Maximum allowed price movement // Convert from a percentage (e.g., 0.5%) to a Percent object // Here, 50 out of 10000 = 0.5% const slippageTolerance = 0.5 // 0.5% slippage tolerance const slippagePct = new Percent(Math.floor(slippageTolerance * 100), 10_000) // 2. deadline (required): Transaction expiry timestamp in seconds // Usually current time + some buffer (e.g., 20 minutes) const deadlineSeconds = 20 * 60 // 20 minutes const currentBlock = await publicClient.getBlock() const currentBlockTimestamp = Number(currentBlock.timestamp) const deadline = currentBlockTimestamp + deadlineSeconds // 3. recipient (required): Address to receive the position NFT // Typically the user's wallet address const userAddress = '0xYourAddressHere' // Replace with actual user address // Create the basic MintOptions object with required fields const mintOptions: MintOptions = { recipient: userAddress, slippageTolerance: slippagePct, deadline: deadline.toString(), // 4. useNative (optional): Use native ETH useNative: ETH_TOKEN.isNative ? Ether.onChain(ETH_TOKEN.chainId) : USDC_TOKEN.isNative ? Ether.onChain(USDC_TOKEN.chainId) : undefined, // 5. batchPermit (optional): For gasless approvals via Permit2 // We'll set this later if needed // 6. hookData (optional): Data for pool hooks // Only needed for pools with custom hooks hookData: '0x', // Default empty bytes // 7-8. For new pools only: // createPool: true, // Uncomment if creating a new pool // sqrtPriceX96: '1234567890123456789', // Initial price, required if createPool is true // 9. For migrations only: // migrate: false, // Normally omitted unless migrating from v3 } ``` | Parameter | Type | Description | Required | | ----------------- | ------------------ | ----------------------------------------------------------- | ------------------ | | slippageTolerance | Percent | Max price movement allowed (for min amount calc) | Yes | | deadline | BigintIsh | Tx expiry timestamp (seconds) | Yes | | recipient | string | Address to receive the position NFT | Yes | | hookData | string (bytes) | Data for pool hook (if applicable) | No | | useNative | NativeCurrency | Use native ETH instead of wrapped token if one is WETH | No | | batchPermit | BatchPermitOptions | Permit2 parameters for gasless token approval | No | | createPool | boolean | Create & initialize pool if not existent | No (default false) | | sqrtPriceX96 | BigintIsh | Initial price (sqrtP) for new pool (required if createPool) | No | | migrate | boolean | Mark as part of v3→v4 migration flow | No | ### Using Permit2 for Gasless Approvals (Optional) The `batchPermit` option allows users to sign a message off-chain to grant token approval, avoiding separate approve transactions. Here's how to implement it: ```typescript // Constants and imports needed for Permit2 // Define necessary constants const CONTRACTS = { PERMIT2: '0x000000000022D473030F116dDEE9F6B43aC78BA3', // Permit2 contract address POSITION_MANAGER: '0x4529a01c7a0410167c5740c487a8de60232617bf.', // Position Manager address (unichain) }; const PERMIT2_ABI = [...]; // Import or define Permit2 ABI const PERMIT2_TYPES = { PermitBatch: [ { name: 'details', type: 'PermitDetails[]' }, { name: 'spender', type: 'address' }, { name: 'sigDeadline', type: 'uint256' } ], PermitDetails: [ { name: 'token', type: 'address' }, { name: 'amount', type: 'uint160' }, { name: 'expiration', type: 'uint48' }, { name: 'nonce', type: 'uint48' } ] }; // Check if we should use Permit2 (application setting or user preference) const usePermit2 = true; // This could be a user setting if (usePermit2) { // Generate Permit2 data only for ERC20 tokens (not needed for native ETH) const permitDetails = []; // Process tokenA if it's not native if (!ETH_TOKEN.isNative) { // Get current nonce from Permit2 contract const [, , nonce] = (await publicClient.readContract({ account: getWalletAccount(), // Your function to get the current wallet address: PERMIT2_ADDRESS, abi: PERMIT2_ABI, functionName: 'allowance', args: [userAddress, ETH_TOKEN.address, POSITION_MANAGER_ADDRESS], })) as [bigint, bigint, bigint]; // Add permit details for this token // Max uint160 value is used as the amount for an unlimited allowance permitDetails.push({ token: ETH_TOKEN.address, amount: (2n ** 160n - 1n).toString(), // Max uint160 expiration: deadline.toString(), nonce: nonce.toString(), }); } // Do the same for tokenB if it's not native if (!USDC_TOKEN.isNative) { const [, , nonce] = (await publicClient.readContract({ account: getWalletAccount(), address: PERMIT2_ADDRESS, abi: PERMIT2_ABI, functionName: 'allowance', args: [userAddress, USDC_TOKEN.address, POSITION_MANAGER_ADDRESS], })) as [bigint, bigint, bigint]; permitDetails.push({ token: USDC_TOKEN.address, amount: (2n ** 160n - 1n).toString(), expiration: deadline.toString(), nonce: nonce.toString(), }); } // If we have any tokens to permit, create and sign the permit message if (permitDetails.length > 0) { // Create permit data const permitData = { details: permitDetails, spender: POSITION_MANAGER_ADDRESS, sigDeadline: deadline.toString(), }; // Sign the permit data with the user's wallet // This requires user interaction to approve the signature const signature = await walletClient.signTypedData({ account, domain: { name: 'Permit2', chainId, verifyingContract: PERMIT2_ADDRESS, }, types: PERMIT2_TYPES, primaryType: 'PermitBatch', message: permitData, }); // Add the permit data and signature to our mint options mintOptions.batchPermit = { owner: userAddress, permitBatch: permitData, signature, }; } } ``` ## Using v4PositionManager to Generate Mint Transaction With a Position object and MintOptions prepared, we can now use the SDK to compute the calldata and value needed for the transaction: ```typescript // Generate transaction parameters // This produces the calldata and value needed for the blockchain transaction const { calldata, value } = V4PositionManager.addCallParameters(position, mintOptions) // Log the results (for debugging) console.log('Calldata:', calldata) console.log('Value:', value) ``` Under the hood, `addCallParameters` builds the necessary function calls to the PositionManager contract: - It encodes a MINT_POSITION command with your position parameters (pool key, tickLower, tickUpper, liquidity) and a SETTLE_PAIR command to pull in the tokens. - The slippageTolerance is applied to calculate amount0Max and amount1Max – these are the maximum token amounts the contract is allowed to take. - If useNative was true, it would also append a SWEEP command for the native token. In case of solidity, please read this [report](https://reports.electisec.com/reports/04-2025-Sickle#2-high---uniswapv4connectoraddliquidity-does-not-reclaim-excess-eth) carefully. - If batchPermit is provided, the SDK will prepend the permit call using the contract's multicall capability. ## Executing the Transaction with Viem After obtaining calldata and value, you need to send the transaction to the blockchain: ```typescript // Function to execute the mint transaction async function executeTransaction() { try { // Send the transaction const txHash = await walletClient.writeContract({ account, chain: chainId, address: POSITION_MANAGER_ADDRESS, abi: POSITION_MANAGER_ABI, functionName: 'multicall', args: [[calldata]], value: BigInt(value), }) // Wait for transaction confirmation const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash, }) } catch (error) { console.error('Transaction failed:', error) } } ``` ## Further Resources - [Uniswap v4 SDK Repository](https://github.com/Uniswap/sdks/tree/main/sdks/v4-sdk) - [Permit2 Documentation](/contracts/permit2/overview) --- ## Executing Multi-Hop Swaps ## Introduction This guide demonstrates how to execute multi-hop swaps on Uniswap V4, allowing you to trade between tokens that might not share a direct pool. Multi-hop swaps route through multiple pools to achieve the desired token exchange, often providing better pricing than attempting direct swaps through less liquid pools. Building on our [single-hop swap guide](./single-hop-swapping.md), this guide will show you how to construct routing paths and execute them efficiently. The guide will cover: 1. Constructing swap paths through multiple pools 2. Executing the multi-hop swap At the end of this guide, you should be able to execute swaps between any two tokens using optimal routing through multiple pools. For this guide, the following Uniswap packages are used: - [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk) - [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core) - [`@uniswap/universal-router-sdk`](https://www.npmjs.com/package/@uniswap/universal-router-sdk) ## Constructing swap paths through multiple pools Let's first define a multi-hop swap configuration. In this example, we'll swap ETH → USDC → USDT. The configuration follows closely from the quoting and single-hop swapping guides. ```typescript const ETH_USDC_POOL_KEY: PoolKey = { currency0: ETH_TOKEN.address, currency1: USDC_TOKEN.address, fee: 3000, tickSpacing: 60, hooks: "0x0000000000000000000000000000000000000000", }; const USDC_USDT_POOL_KEY: PoolKey = { currency0: USDC_TOKEN.address, currency1: USDT_TOKEN.address, fee: 10, tickSpacing: 1, hooks: "0x0000000000000000000000000000000000000000", }; export const CurrentConfig: SwapExactIn = { currencyIn: ETH_TOKEN.address, path: encodeMultihopExactInPath( [ETH_USDC_POOL_KEY, USDC_USDT_POOL_KEY], ETH_TOKEN.address ), amountIn: ethers.utils.parseUnits('1', ETH_TOKEN.decimals).toString(), amountOutMinimum: "minAmountOut", // Change according to the slippage desired } ``` Uniswap V4 uses a specific format for encoding multi-hop paths. Each hop in the path requires: ```typescript type PathKey = { intermediateCurrency: string; fee: number; tickSpacing: number; hooks: string; hookData: string; }; ``` We can encode the path using a function like: ```typescript export function encodeMultihopExactInPath( poolKeys: PoolKey[], currencyIn: string ): PathKey[] { const pathKeys: PathKey[] = [] let currentCurrencyIn = currencyIn for (let i = 0; i < poolKeys.length; i++) { // Determine the output currency for this hop const currencyOut = currentCurrencyIn === poolKeys[i].currency0 ? poolKeys[i].currency1 : poolKeys[i].currency0 // Create path key for this hop const pathKey: PathKey = { intermediateCurrency: currencyOut, fee: poolKeys[i].fee, tickSpacing: poolKeys[i].tickSpacing, hooks: poolKeys[i].hooks, hookData: '0x' } pathKeys.push(pathKey) currentCurrencyIn = currencyOut // Output becomes input for next hop } return pathKeys } ``` ## Executing the multi-hop swap We'll use the same contract addresses and ABIs from the single-hop guide and construct the **ethers** `Contract` for them: ```typescript const UNIVERSAL_ROUTER_ADDRESS = "0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af" const PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3" // ABIs remain the same as in single-hop guide const UNIVERSAL_ROUTER_ABI = [/* ... */] const ERC20_ABI = [/* ... */] const PERMIT2_ABI = [/* ... */] ``` The main function for executing multi-hop swaps is very similar to the single-hop guide as well. The only difference is that the first action to the Universal Router is `SWAP_EXACT_IN` instead of `SWAP_EXACT_IN_SINGLE`. ```typescript const v4Planner = new V4Planner() const routePlanner = new RoutePlanner() const deadline = Math.floor(Date.now() / 1000) + 3600 v4Planner.addAction(Actions.SWAP_EXACT_IN, [CurrentConfig]); v4Planner.addAction(Actions.SETTLE_ALL, [ETH_USDC_POOL_KEY.currency0, CurrentConfig.amountIn]); v4Planner.addAction(Actions.TAKE_ALL, [USDC_USDT_POOL_KEY.currency1, CurrentConfig.amountOutMinimum]); const encodedActions = v4Planner.finalize() routePlanner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params]) // Only needed for native ETH as input currency swaps const txOptions: any = { value: CurrentConfig.amountIn } const tx = await universalRouter.execute( routePlanner.commands, [encodedActions], deadline, txOptions ) const receipt = await tx.wait() console.log('Multi-hop swap completed! Transaction hash:', receipt.transactionHash) ``` The token approvals for ERC20 token swaps remain the same as the [single-hop swapping guide](./02-single-hop-swapping.md). ## Next Steps Now that you're familiar with trading, consider checking out our next guides on [pooling liquidity](../liquidity/01-pool-data.md) to Uniswap! --- ## Getting a Quote ## Introduction This guide will cover how to get the current quotes for any token pair on the Uniswap protocol. In this example we will use `quoteExactInputSingle` to get a quote for the pair **ETH - USDC**. The inputs are **poolKey**, **zeroForOne**, **exactAmount** and **hookData**. The guide will cover: 1. Constructing the `PoolKey` and swap parameters 2. Referencing the `Quoter` contract and getting a quote At the end of the guide, we should be able to fetch the output for the given token pair and input amount. For this guide, the following Uniswap packages are used: - [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk) - [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core) ## Constructing the PoolKey and Swap parameters We will first create an example configuration `CurrentConfig` in `config.ts`. For this example, we are using the [0.05% ETH - USDC pool](https://app.uniswap.org/explore/pools/ethereum/0x21c67e77068de97969ba93d4aab21826d33ca12bb9f565d8496e8fda8a82ca27) which has the format: ```typescript export const CurrentConfig: SwapExactInSingle = { poolKey: { currency0: ETH_TOKEN.address, currency1: USDC_TOKEN.address, fee: 500, tickSpacing: 10, hooks: "0x0000000000000000000000000000000000000000", }, zeroForOne: true, amountIn: parseUnits('1', ETH_TOKEN.decimals).toString(), amountOutMinimum: "0", hookData: '0x00' } ``` The pool used is defined by a pair of tokens in `constants.ts`. You can also change these two tokens and the other pool parameters in the config, just make sure a pool actually exists for your configuration. Check out the top pools on [Uniswap](https://app.uniswap.org/#/pools). ```typescript export const ETH_TOKEN = new Token( ChainId.MAINNET, '0x0000000000000000000000000000000000000000', 18, 'ETH', 'Ether' ) export const USDC_TOKEN = new Token( ChainId.MAINNET, '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 6, 'USDC', 'USDC' ) ``` ## Referencing the Quoter contract and getting a quote To get quotes for trades, Uniswap has deployed a **Quoter Contract**. We will use this contract to fetch the output amount we can expect for our trade, without actually executing the trade. Now, we need to construct an instance of an **ethers** `Contract` for our Quoter contract in order to interact with it: ```typescript const quoterContract = new ethers.Contract( QUOTER_CONTRACT_ADDRESS, QUOTER_ABI, // Import or define the ABI for Quoter contract new JsonRpcProvider("RPC") // Provide the right RPC address for the chain ) ``` We get the `QUOTE_CONTRACT_ADDRESS` for our chain from [Uniswap Deployments](https://docs.uniswap.org/contracts/v4/deployments). We can now use our Quoter contract to obtain the quote. In an ideal world, the quoter functions would be `view` functions, which would make them very easy to query on-chain with minimal gas costs. However, the Uniswap V4 Quoter contracts rely on state-changing calls designed to be reverted to return the desired data. This means calling the quoter will be very expensive and **should not be called on-chain.** To get around this difficulty, we can use the `callStatic` method provided by the **ethers.js** `Contract` instances. This is a useful method that submits a state-changing transaction to an Ethereum node, but asks the node to simulate the state change, rather than to execute it. Our script can then return the result of the simulated state change: ```typescript const quotedAmountOut = await quoterContract.callStatic.quoteExactInputSingle({ poolKey: CurrentConfig.poolKey, zeroForOne: CurrentConfig.zeroForOne, exactAmount: CurrentConfig.amountIn, hookData: CurrentConfig.hookData, }) console.log(formatUnits(quotedAmountOut.amountOut, USDC_TOKEN.decimals)); ``` The result of the call is the number of output tokens you would receive for the quoted swap. It should be noted that `quoteExactInputSingle` is only 1 of 4 different methods that the quoter offers: 1. `quoteExactInputSingle` - given an input amount, produce a quote of the output amount for a swap on a single pool 2. `quoteExactInput` - given an input amount, produce a quote for the output amount a swap over multiple pools 3. `quoteExactOutputSingle` - given a desired output amount, produce a quote for the input amount on a swap over a single pool 4. `quoteExactOutput` - given a desired output amount, produce a quote for the input amount in for a swap over multiple pools If we want to trade two tokens that do not share a pool with each other, we will need to make swaps over multiple pools. This is where the `quoteExactInput` and `quoteExactOutput` methods come in. For the `exactOutput` and `exactOutputSingle` methods, we need to keep in mind that a pool can not give us more than the amount of Tokens it holds. If we try to get a quote on an output of 100 ETH from a pool that only holds 50 ETH, the function call will fail. --- ## Executing a Single-Hop Swap ## Introduction This guide will build off our [quoting guide](./quoting.md) and show how to use a quote to construct and execute a trade on the Uniswap v4 protocol. In this example we will trade between two tokens: **ETH and USDC**. The guide will cover: 1. Setting up swap parameters and pool configuration 2. Using Universal Router and executing a single-hop swap At the end of this guide, you should be able to execute swaps between any two tokens using a single pool on Uniswap V4. For this guide, the following Uniswap packages are used: - [`@uniswap/v4-sdk`](https://www.npmjs.com/package/@uniswap/v4-sdk) - [`@uniswap/sdk-core`](https://www.npmjs.com/package/@uniswap/sdk-core) - [`@uniswap/universal-router-sdk`](https://www.npmjs.com/package/@uniswap/universal-router-sdk) ## Setting up Swap Configuration First, let's define our swap configuration. We will use the same pool structure from the quoting guide: ```typescript export const CurrentConfig: SwapExactInSingle = { poolKey: { currency0: ETH_TOKEN.address, currency1: USDC_TOKEN.address, fee: 500, tickSpacing: 10, hooks: "0x0000000000000000000000000000000000000000", }, zeroForOne: true, // The direction of swap is ETH to USDC. Change it to 'false' for the reverse direction amountIn: ethers.utils.parseUnits('1', ETH_TOKEN.decimals).toString(), amountOutMinimum: "minAmountOut", // Change according to the slippage desired hookData: '0x00' } ``` Like the quoting guide, the pool used is defined by a pair of tokens in `constants.ts`. You can change these two tokens and the other pool parameters in the config as long as a pool actually exists for that configuration. ## Using Universal Router and executing a single-hop swap The Universal Router is a flexible, gas-efficient contract designed to execute complex swap operations across various protocols, including Uniswap v4. It serves as an intermediary between users and the Uniswap v4 PoolManager, handling the intricacies of swap execution. So, we construct an instance of an **ethers** `Contract` for the Universal Router contract in order to interact with it: ```typescript const UNIVERSAL_ROUTER_ADDRESS = "0x66a9893cc07d91d95644aedd05d03f95e1dba8af" // Change the Universal Router address as per the chain const UNIVERSAL_ROUTER_ABI = [ { inputs: [ { internalType: "bytes", name: "commands", type: "bytes" }, { internalType: "bytes[]", name: "inputs", type: "bytes[]" }, { internalType: "uint256", name: "deadline", type: "uint256" }, ], name: "execute", outputs: [], stateMutability: "payable", type: "function", }, ] const universalRouter = new ethers.Contract( UNIVERSAL_ROUTER_ADDRESS, UNIVERSAL_ROUTER_ABI, signer ) ``` We can get the `UNIVERSAL_ROUTER_ADDRESS` for our chain from [Uniswap Deployments](/contracts/v4/deployments). A signer object can be created like this: ```typescript const provider = new ethers.providers.JsonRpcProvider("RPC"); const signer = new ethers.Wallet( "YOUR PRIVATE KEY", provider ); ``` Now, let's implement the main function that handles the swap. When encoding a swap command for the Universal Router, we need to choose between two types of swaps: - Exact Input Swaps: Use this swap-type when you know the exact amount of tokens you want to swap in, and you're willing to accept any amount of output tokens above your minimum. This is common when you want to sell a specific amount of tokens. - Exact Output Swaps: Use this swap-type when you need a specific amount of output tokens, and you're willing to spend up to a maximum amount of input tokens. This is useful when you need to acquire a precise amount of tokens, for example, to repay a loan or meet a specific requirement. We will be doing an Exact Input swap in this example. ```typescript const v4Planner = new V4Planner() const routePlanner = new RoutePlanner() // Set deadline (1 hour from now) const deadline = Math.floor(Date.now() / 1000) + 3600 v4Planner.addAction(Actions.SWAP_EXACT_IN_SINGLE, [CurrentConfig]); v4Planner.addAction(Actions.SETTLE_ALL, [CurrentConfig.poolKey.currency0, CurrentConfig.amountIn]); v4Planner.addAction(Actions.TAKE_ALL, [CurrentConfig.poolKey.currency1, CurrentConfig.amountOutMinimum]); const encodedActions = v4Planner.finalize() routePlanner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params]) // Only needed for native ETH as input currency swaps const txOptions: any = { value: CurrentConfig.amountIn } const tx = await universalRouter.execute( routePlanner.commands, [encodedActions], deadline, txOptions ) const receipt = await tx.wait() console.log('Swap completed! Transaction hash:', receipt.transactionHash) ``` The actions in the planner define the sequence of operations that will be performed in our v4 swap: - `SWAP_EXACT_IN_SINGLE`: This action specifies that we want to perform an exact input swap using a single pool. - `SETTLE_ALL`: This action ensures all input tokens involved in the swap are properly paid. This is part of v4's settlement pattern for handling token transfers. - `TAKE_ALL`: This final action collects all output tokens after the swap is complete. The sequence of these actions is important as they define the complete flow of our swap operation from start to finish. The input and output currencies should be exchanged for the `SETTLE_ALL` and `TAKE_ALL` actions if the direction of the swap is reversed. The `V4_SWAP` command tells the Universal Router that we want to perform a swap on a Uniswap v4 pool. ## Handling Token Approvals for ERC20 Swaps When swapping ERC20 tokens, we need to set up approvals through Permit2. So, we construct an instance of an **ethers** `Contract` for the Permit2 contract in order to interact with it: ```typescript const permit2Contract = new ethers.Contract( PERMIT2_ADDRESS, PERMIT2_ABI, signer ) ``` Create a similar one for the ERC20 token contract. If enough allowances have not been provided or the deadline has expired, we first need to approve Permit2 as a spender on the ERC20 token and then approve the Universal Router on Permit2. ```typescript const tx1 = await erc20Contract.approve(PERMIT2_ADDRESS, ethers.constants.MaxUint256) const tx2 = await permit2Contract.approve( tokenAddress, UNIVERSAL_ROUTER_ADDRESS, ethers.BigNumber.from(2).pow(160).sub(1), // MAX_UINT160 deadline ) ``` The rest of the swap process remains the same. ## Next Steps Now that you understand single-hop swaps, you might want to explore [multi-hop swaps](./03-multi-hop-swapping.md) for trading between tokens without direct pools or enough liquidity. --- ## Overview(3) # The Uniswap v4 SDK > **Welcome to the v4 Uniswap SDK!** The Uniswap v4 SDK provides abstractions to assist you with interacting with the Uniswap v4 smart contracts in a Typescript/Javascript environment (e.g. websites, node scripts). It makes use of the [**Core SDK**](../core/overview.md) to gain access to abstractions that are common amongst the Uniswap SDKs. With the SDK, you can add/remove liquidity, collect fees like what you will usually do with v3 SDK, but more with the extra functionalities from hooks introduced in v4! For complete documentation of the SDK's offerings, see the [**Technical Reference**](./reference/overview.md). ## Installation To interact with the v4 SDK we recommend installing through npm: ```bash npm i --save @uniswap/v4-sdk npm i --save @uniswap/sdk-core ``` ## What's Different in v4 The Uniswap v4 SDK introduces some major changes that fundamentally alter how developers interact with Uniswap. Understanding these key differences is essential for successful v4 development. ## Key Changes ### Universal Router Requirement for Swapping **What changed**: v3 allowed direct calls to the v3 Swap Router contract. v4 requires all swapping operations to go through the Universal Router. **Why**: v4's singleton PoolManager architecture and flash accounting system require a different interaction pattern. The v4Planner batches operations and encodes them for the Universal Router - you cannot make direct swap calls. **Key differences**: - All swaps must be "planned" using v4Planner, even single swaps - Operations use new patterns: SETTLE (pay tokens) and TAKE (receive tokens) - Enables efficient multi-step operations and cross-protocol routes **Impact**: This enables more efficient multi-step operations in a single transaction. ### StateView Contract Introduction **Why it exists**: v4 uses a singleton PoolManager that tracks all pools in one contract, unlike v3's separate pool contracts. **What it means**: The StateView contract wraps the PoolManager's state reading functions with a dedicated view-only interface. Instead of calling the PoolManager directly for state queries, you use StateView for cleaner, more organized access to pool data like slot0, tick info, liquidity, and position information. **Impact**: Provides a dedicated, organized interface for off-chain clients to read pool state data. ### Position Fetching Changed **What changed**: v3 allowed easy enumeration of user positions on-chain. v4 provides no way to get all of a user's position IDs directly from the contracts. Additionally, position information is packed into a single uint256 value for efficiency, requiring decoding to extract individual fields like liquidity, fee growth, and tick ranges. **Why it's different**: This design choice means position enumeration must happen off-chain through event indexing. **Impact**: Applications must choose and implement indexing solutions to know which positions a user owns. ### Fee Collection Behavior Changed **What changed**: v3 had an explicit `collect()` function for fee collection. v4 has no standalone collect function - fees are automatically collected and distributed when you modify positions. **New pattern**: - Fees automatically roll over when increasing/decreasing liquidity - To collect fees without modifying position size, you must modify the position with zero change (e.g., `modifyLiquidity(positionId, 0)`) - StateView contract must be used to query the fee growth inside in order to calculate the exact amount of fees owed **Impact**: Fee collection logic must be redesigned around position modifications rather than explicit collect() calls. ### Quick Comparison | Feature | v3 | v4 | |---------|----|----| | Swapping | Direct router calls | Universal Router | | Pool State | Individual pool contracts | StateView contract | | Position Discovery | On-chain enumeration | Off-chain indexing | | Fee Collection | Explicit collect() | Automatic on modification | ### What This Means for Developers ### Migration Requirements 1. **Restructure all swaps** to use Universal Router with v4Planner 2. **Build position indexing systems** using event logs and subgraphs for position discovery 3. **Redesign fee collection logic** to use position modifications instead of explicit collect() calls 4. **Implement StateView integration** for all pool state queries instead of direct PoolManager calls ### Development Impact - **Universal Router**: All swaps must be batched, but enables complex multi-step operations - **Position tracking**: Requires additional infrastructure - **Fee collection**: Simpler in some cases (automatic), more complex in others (zero-change modifications) - **StateView**: Cleaner interface for state queries ## Learning Path To get started with v4 SDK development, follow these guides based on the key changes: ### 1. Swapping Learn how to restructure swaps using Universal Router integration. **Guides**: - [Getting a Quote](/sdk/v4/guides/swaps/quoting) - [Executing a Single-Hop Swap](/sdk/v4/guides/swaps/single-hop-swapping) - [Executing Multi-Hop Swaps](/sdk/v4/guides/swaps/multi-hop-swapping) ### 2. Position Management (Off-chain Indexing + Fee Collection) Understand position tracking systems and the new automatic fee collection patterns. **Guides**: - [Minting a Position](/sdk/v4/guides/liquidity/position-minting) - [Fetching Positions](/sdk/v4/guides/liquidity/position-fetching) - [Collecting Fees](/sdk/v4/guides/liquidity/collect-fees) - [Adding and Removing Liquidity](/sdk/v4/guides/liquidity/add-remove-liquidity) ### 3. Advanced Features (StateView + Pool Creation) Explore efficient state queries and pool creation with the new architecture. **Guides**: - [Fetching Pool Data](/sdk/v4/guides/advanced/pool-data) - [Create Pool](/sdk/v4/guides/advanced/create-pool) ## Developer Links - [**v4 SDK GitHub Repo**](https://github.com/Uniswap/sdks/tree/main/sdks/v4-sdk) - [**Core SDK GitHub Repo**](https://github.com/Uniswap/sdk-core) - [**v4 SDK NPM Package**](https://www.npmjs.com/package/@uniswap/v4-sdk) [![Unit Tests](https://github.com/Uniswap/uniswap-v3-sdk/workflows/Unit%20Tests/badge.svg)](https://github.com/Uniswap/sdks/actions?query=workflow%3A%22%22Code+Quality+Checks%22%22++) [![Lint](https://github.com/Uniswap/uniswap-v3-sdk/workflows/Lint/badge.svg)](https://github.com/Uniswap/sdks/actions?query=workflow%3A%22%22Code+Quality+Checks%22%22++) [![npm version](https://img.shields.io/npm/v/@uniswap/v4-sdk/latest.svg)](https://www.npmjs.com/package/@uniswap/v4-sdk/v/latest) [![npm bundle size (scoped version)](https://img.shields.io/bundlephobia/minzip/@uniswap/v4-sdk/latest.svg)](https://bundlephobia.com/result?p=@uniswap/v4-sdk@latest) [![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.com/channels/597638925346930701/607978109089611786) --- ## Hook [@uniswap/v4-sdk](../overview.md) / Hook Defined in: [utils/hook.ts:40](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L40) ## Constructors ### new Hook() > **new Hook**(): [`Hook`](Hook.md) #### Returns [`Hook`](Hook.md) ## Methods ### hasDonatePermissions() > `static` **hasDonatePermissions**(`address`): `boolean` Defined in: [utils/hook.ts:91](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L91) #### Parameters | Parameter | Type | | ------ | ------ | | `address` | `string` | #### Returns `boolean` *** ### hasInitializePermissions() > `static` **hasInitializePermissions**(`address`): `boolean` Defined in: [utils/hook.ts:66](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L66) #### Parameters | Parameter | Type | | ------ | ------ | | `address` | `string` | #### Returns `boolean` *** ### hasLiquidityPermissions() > `static` **hasLiquidityPermissions**(`address`): `boolean` Defined in: [utils/hook.ts:74](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L74) #### Parameters | Parameter | Type | | ------ | ------ | | `address` | `string` | #### Returns `boolean` *** ### hasPermission() > `static` **hasPermission**(`address`, `hookOption`): `boolean` Defined in: [utils/hook.ts:61](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L61) #### Parameters | Parameter | Type | | ------ | ------ | | `address` | `string` | | `hookOption` | [`HookOptions`](../enumerations/HookOptions.md) | #### Returns `boolean` *** ### hasSwapPermissions() > `static` **hasSwapPermissions**(`address`): `boolean` Defined in: [utils/hook.ts:85](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L85) #### Parameters | Parameter | Type | | ------ | ------ | | `address` | `string` | #### Returns `boolean` *** ### permissions() > `static` **permissions**(`address`): [`HookPermissions`](../overview.md#hookpermissions) Defined in: [utils/hook.ts:41](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L41) #### Parameters | Parameter | Type | | ------ | ------ | | `address` | `string` | #### Returns [`HookPermissions`](../overview.md#hookpermissions) --- ## Pool(Classes) [@uniswap/v4-sdk](../overview.md) / Pool Defined in: [entities/pool.ts:33](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L33) Represents a V4 pool ## Constructors ### new Pool() > **new Pool**(`currencyA`, `currencyB`, `fee`, `tickSpacing`, `hooks`, `sqrtRatioX96`, `liquidity`, `tickCurrent`, `ticks`): [`Pool`](Pool.md) Defined in: [entities/pool.ts:103](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L103) Construct a pool #### Parameters | Parameter | Type | Default value | Description | | ------ | ------ | ------ | ------ | | `currencyA` | `Currency` | `undefined` | One of the currencys in the pool | | `currencyB` | `Currency` | `undefined` | The other currency in the pool | | `fee` | `number` | `undefined` | The fee in hundredths of a bips of the input amount of every swap that is collected by the pool | | `tickSpacing` | `number` | `undefined` | The tickSpacing of the pool | | `hooks` | `string` | `undefined` | The address of the hook contract | | `sqrtRatioX96` | `BigintIsh` | `undefined` | The sqrt of the current ratio of amounts of currency1 to currency0 | | `liquidity` | `BigintIsh` | `undefined` | The current value of in range liquidity | | `tickCurrent` | `number` | `undefined` | The current tick of the pool | | `ticks` | `TickDataProvider` | (`Tick` | `TickConstructorArgs`)[] | `NO_TICK_DATA_PROVIDER_DEFAULT` | - | #### Returns [`Pool`](Pool.md) ## Properties ### currency0 > `readonly` **currency0**: `Currency` Defined in: [entities/pool.ts:34](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L34) *** ### currency1 > `readonly` **currency1**: `Currency` Defined in: [entities/pool.ts:35](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L35) *** ### fee > `readonly` **fee**: `number` Defined in: [entities/pool.ts:36](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L36) *** ### hooks > `readonly` **hooks**: `string` Defined in: [entities/pool.ts:39](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L39) *** ### liquidity > `readonly` **liquidity**: `JSBI` Defined in: [entities/pool.ts:40](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L40) *** ### poolId > `readonly` **poolId**: `string` Defined in: [entities/pool.ts:44](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L44) *** ### poolKey > `readonly` **poolKey**: [`PoolKey`](../overview.md#poolkey) Defined in: [entities/pool.ts:43](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L43) *** ### sqrtRatioX96 > `readonly` **sqrtRatioX96**: `JSBI` Defined in: [entities/pool.ts:38](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L38) *** ### tickCurrent > `readonly` **tickCurrent**: `number` Defined in: [entities/pool.ts:41](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L41) *** ### tickDataProvider > `readonly` **tickDataProvider**: `TickDataProvider` Defined in: [entities/pool.ts:42](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L42) *** ### tickSpacing > `readonly` **tickSpacing**: `number` Defined in: [entities/pool.ts:37](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L37) ## Accessors ### chainId #### Get Signature > **get** **chainId**(): `number` Defined in: [entities/pool.ts:214](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L214) Returns the chain ID of the currencies in the pool. ##### Returns `number` *** ### currency0Price #### Get Signature > **get** **currency0Price**(): `Price`\<`Currency`, `Currency`\> Defined in: [entities/pool.ts:166](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L166) Returns the current mid price of the pool in terms of currency0, i.e. the ratio of currency1 over currency0 ##### Returns `Price`\<`Currency`, `Currency`\> *** ### currency1Price #### Get Signature > **get** **currency1Price**(): `Price`\<`Currency`, `Currency`\> Defined in: [entities/pool.ts:185](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L185) Returns the current mid price of the pool in terms of currency1, i.e. the ratio of currency0 over currency1 ##### Returns `Price`\<`Currency`, `Currency`\> *** ### token0 #### Get Signature > **get** **token0**(): `Currency` Defined in: [entities/pool.ts:143](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L143) backwards compatibility with v2/3 sdks ##### Returns `Currency` *** ### token0Price #### Get Signature > **get** **token0Price**(): `Price`\<`Currency`, `Currency`\> Defined in: [entities/pool.ts:178](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L178) backwards compatibility with v2/3 sdks ##### Returns `Price`\<`Currency`, `Currency`\> *** ### token1 #### Get Signature > **get** **token1**(): `Currency` Defined in: [entities/pool.ts:146](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L146) ##### Returns `Currency` *** ### token1Price #### Get Signature > **get** **token1Price**(): `Price`\<`Currency`, `Currency`\> Defined in: [entities/pool.ts:197](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L197) backwards compatibility with v2/3 sdks ##### Returns `Price`\<`Currency`, `Currency`\> ## Methods ### getInputAmount() > **getInputAmount**(`outputAmount`, `sqrtPriceLimitX96`?): `Promise`\<[`CurrencyAmount`\<`Currency`\>, [`Pool`](Pool.md)]> Defined in: [entities/pool.ts:257](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L257) Given a desired output amount of a currency, return the computed input amount and a pool with state updated after the trade Works only for vanilla hookless v3 pools, otherwise throws an error #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `outputAmount` | `CurrencyAmount`\<`Currency`\> | the output amount for which to quote the input amount | | `sqrtPriceLimitX96`? | `JSBI` | The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this value after the swap. If one for zero, the price cannot be greater than this value after the swap | #### Returns `Promise`\<[`CurrencyAmount`\<`Currency`\>, [`Pool`](Pool.md)]> The input amount and the pool with updated state *** ### getOutputAmount() > **getOutputAmount**(`inputAmount`, `sqrtPriceLimitX96`?): `Promise`\<[`CurrencyAmount`\<`Currency`\>, [`Pool`](Pool.md)]> Defined in: [entities/pool.ts:219](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L219) Works only for vanilla hookless v3 pools, otherwise throws an error #### Parameters | Parameter | Type | | ------ | ------ | | `inputAmount` | `CurrencyAmount`\<`Currency`\> | | `sqrtPriceLimitX96`? | `JSBI` | #### Returns `Promise`\<[`CurrencyAmount`\<`Currency`\>, [`Pool`](Pool.md)]> *** ### getPoolId() > `static` **getPoolId**(`currencyA`, `currencyB`, `fee`, `tickSpacing`, `hooks`): `string` Defined in: [entities/pool.ts:71](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L71) #### Parameters | Parameter | Type | | ------ | ------ | | `currencyA` | `Currency` | | `currencyB` | `Currency` | | `fee` | `number` | | `tickSpacing` | `number` | | `hooks` | `string` | #### Returns `string` *** ### getPoolKey() > `static` **getPoolKey**(`currencyA`, `currencyB`, `fee`, `tickSpacing`, `hooks`): [`PoolKey`](../overview.md#poolkey) Defined in: [entities/pool.ts:49](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L49) #### Parameters | Parameter | Type | | ------ | ------ | | `currencyA` | `Currency` | | `currencyB` | `Currency` | | `fee` | `number` | | `tickSpacing` | `number` | | `hooks` | `string` | #### Returns [`PoolKey`](../overview.md#poolkey) *** ### involvesCurrency() > **involvesCurrency**(`currency`): `boolean` Defined in: [entities/pool.ts:155](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L155) Returns true if the currency is either currency0 or currency1 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `currency` | `Currency` | The currency to check | #### Returns `boolean` True if currency is either currency0 or currency1 *** ### involvesToken() > **involvesToken**(`currency`): `boolean` Defined in: [entities/pool.ts:159](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L159) backwards compatibility with v2/3 sdks #### Parameters | Parameter | Type | | ------ | ------ | | `currency` | `Currency` | #### Returns `boolean` *** ### priceOf() > **priceOf**(`currency`): `Price`\<`Currency`, `Currency`\> Defined in: [entities/pool.ts:206](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L206) Return the price of the given currency in terms of the other currency in the pool. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `currency` | `Currency` | The currency to return price of | #### Returns `Price`\<`Currency`, `Currency`\> The price of the given currency, in terms of the other. --- ## Position(Classes) [@uniswap/v4-sdk](../overview.md) / Position Defined in: [entities/position.ts:23](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L23) Represents a position on a Uniswap V4 Pool ## Dev Similar to the V3 implementation - using Currency instead of Token - keep in mind that Pool and liquidity must be fetched from the pool manager ## Constructors ### new Position() > **new Position**(`__namedParameters`): [`Position`](Position.md) Defined in: [entities/position.ts:41](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L41) Constructs a position for a given pool with the given liquidity #### Parameters | Parameter | Type | | ------ | ------ | | `__namedParameters` | `PositionConstructorArgs` | #### Returns [`Position`](Position.md) ## Properties ### liquidity > `readonly` **liquidity**: `JSBI` Defined in: [entities/position.ts:27](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L27) *** ### pool > `readonly` **pool**: [`Pool`](Pool.md) Defined in: [entities/position.ts:24](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L24) *** ### tickLower > `readonly` **tickLower**: `number` Defined in: [entities/position.ts:25](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L25) *** ### tickUpper > `readonly` **tickUpper**: `number` Defined in: [entities/position.ts:26](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L26) ## Accessors ### amount0 #### Get Signature > **get** **amount0**(): `CurrencyAmount`\<`Currency`\> Defined in: [entities/position.ts:69](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L69) Returns the amount of token0 that this position's liquidity could be burned for at the current pool price ##### Returns `CurrencyAmount`\<`Currency`\> *** ### amount1 #### Get Signature > **get** **amount1**(): `CurrencyAmount`\<`Currency`\> Defined in: [entities/position.ts:101](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L101) Returns the amount of token1 that this position's liquidity could be burned for at the current pool price ##### Returns `CurrencyAmount`\<`Currency`\> *** ### mintAmounts #### Get Signature > **get** **mintAmounts**(): `Readonly`\<\{ `amount0`: `JSBI`; `amount1`: `JSBI`; \}\> Defined in: [entities/position.ts:272](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L272) Returns the minimum amounts that must be sent in order to mint the amount of liquidity held by the position at the current price for the pool ##### Returns `Readonly`\<\{ `amount0`: `JSBI`; `amount1`: `JSBI`; \}\> *** ### token0PriceLower #### Get Signature > **get** **token0PriceLower**(): `Price`\<`Currency`, `Currency`\> Defined in: [entities/position.ts:55](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L55) Returns the price of token0 at the lower tick ##### Returns `Price`\<`Currency`, `Currency`\> *** ### token0PriceUpper #### Get Signature > **get** **token0PriceUpper**(): `Price`\<`Currency`, `Currency`\> Defined in: [entities/position.ts:62](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L62) Returns the price of token0 at the upper tick ##### Returns `Price`\<`Currency`, `Currency`\> ## Methods ### burnAmountsWithSlippage() > **burnAmountsWithSlippage**(`slippageTolerance`): `Readonly`\<\{ `amount0`: `JSBI`; `amount1`: `JSBI`; \}\> Defined in: [entities/position.ts:223](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L223) Returns the minimum amounts that should be requested in order to safely burn the amount of liquidity held by the position with the given slippage tolerance #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `slippageTolerance` | `Percent` | tolerance of unfavorable slippage from the current price | #### Returns `Readonly`\<\{ `amount0`: `JSBI`; `amount1`: `JSBI`; \}\> The amounts, with slippage *** ### fromAmount0() > `static` **fromAmount0**(`__namedParameters`): [`Position`](Position.md) Defined in: [entities/position.ts:402](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L402) Computes a position with the maximum amount of liquidity received for a given amount of token0, assuming an unlimited amount of token1 #### Parameters | Parameter | Type | | ------ | ------ | | `__namedParameters` | \{ `amount0`: `BigintIsh`; `pool`: [`Pool`](Pool.md); `tickLower`: `number`; `tickUpper`: `number`; `useFullPrecision`: `boolean`; \} | | `__namedParameters.amount0` | `BigintIsh` | | `__namedParameters.pool` | [`Pool`](Pool.md) | | `__namedParameters.tickLower` | `number` | | `__namedParameters.tickUpper` | `number` | | `__namedParameters.useFullPrecision` | `boolean` | #### Returns [`Position`](Position.md) The position *** ### fromAmount1() > `static` **fromAmount1**(`__namedParameters`): [`Position`](Position.md) Defined in: [entities/position.ts:426](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L426) Computes a position with the maximum amount of liquidity received for a given amount of token1, assuming an unlimited amount of token0 #### Parameters | Parameter | Type | | ------ | ------ | | `__namedParameters` | \{ `amount1`: `BigintIsh`; `pool`: [`Pool`](Pool.md); `tickLower`: `number`; `tickUpper`: `number`; \} | | `__namedParameters.amount1` | `BigintIsh` | | `__namedParameters.pool` | [`Pool`](Pool.md) | | `__namedParameters.tickLower` | `number` | | `__namedParameters.tickUpper` | `number` | #### Returns [`Position`](Position.md) The position *** ### fromAmounts() > `static` **fromAmounts**(`__namedParameters`): [`Position`](Position.md) Defined in: [entities/position.ts:360](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L360) Computes the maximum amount of liquidity received for a given amount of token0, token1, and the prices at the tick boundaries. #### Parameters | Parameter | Type | | ------ | ------ | | `__namedParameters` | \{ `amount0`: `BigintIsh`; `amount1`: `BigintIsh`; `pool`: [`Pool`](Pool.md); `tickLower`: `number`; `tickUpper`: `number`; `useFullPrecision`: `boolean`; \} | | `__namedParameters.amount0` | `BigintIsh` | | `__namedParameters.amount1` | `BigintIsh` | | `__namedParameters.pool` | [`Pool`](Pool.md) | | `__namedParameters.tickLower` | `number` | | `__namedParameters.tickUpper` | `number` | | `__namedParameters.useFullPrecision` | `boolean` | #### Returns [`Position`](Position.md) The amount of liquidity for the position *** ### mintAmountsWithSlippage() > **mintAmountsWithSlippage**(`slippageTolerance`): `Readonly`\<\{ `amount0`: `JSBI`; `amount1`: `JSBI`; \}\> Defined in: [entities/position.ts:159](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L159) Returns the maximum amount of token0 and token1 that must be sent in order to safely mint the amount of liquidity held by the position with the given slippage tolerance #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `slippageTolerance` | `Percent` | Tolerance of unfavorable slippage from the current price | #### Returns `Readonly`\<\{ `amount0`: `JSBI`; `amount1`: `JSBI`; \}\> The amounts, with slippage #### Dev In v4, minting and increasing is protected by maximum amounts of token0 and token1. *** ### permitBatchData() > **permitBatchData**(`slippageTolerance`, `spender`, `nonce`, `deadline`): [`AllowanceTransferPermitBatch`](../interfaces/AllowanceTransferPermitBatch.md) Defined in: [entities/position.ts:321](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/position.ts#L321) Returns the AllowanceTransferPermitBatch for adding liquidity to a position #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `slippageTolerance` | `Percent` | The amount by which the price can 'slip' before the transaction will revert | | `spender` | `string` | The spender of the permit (should usually be the PositionManager) | | `nonce` | `BigintIsh` | A valid permit2 nonce | | `deadline` | `BigintIsh` | The deadline for the permit | #### Returns [`AllowanceTransferPermitBatch`](../interfaces/AllowanceTransferPermitBatch.md) --- ## Route [@uniswap/v4-sdk](../overview.md) / Route Defined in: [entities/route.ts:12](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L12) Represents a list of pools through which a swap can occur ## Type Parameters | Type Parameter | Description | | ------ | ------ | | `TInput` *extends* `Currency` | The input currency | | `TOutput` *extends* `Currency` | The output currency | ## Constructors ### new Route() > **new Route**\<`TInput`, `TOutput`\>(`pools`, `input`, `output`): [`Route`](Route.md)\<`TInput`, `TOutput`\> Defined in: [entities/route.ts:28](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L28) Creates an instance of route. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `pools` | [`Pool`](Pool.md)[] | An array of `Pool` objects, ordered by the route the swap will take | | `input` | `TInput` | The input currency | | `output` | `TOutput` | The output currency | #### Returns [`Route`](Route.md)\<`TInput`, `TOutput`\> ## Properties ### currencyPath > `readonly` **currencyPath**: `Currency`[] Defined in: [entities/route.ts:14](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L14) *** ### input > `readonly` **input**: `TInput` Defined in: [entities/route.ts:15](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L15) *** ### output > `readonly` **output**: `TOutput` Defined in: [entities/route.ts:16](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L16) *** ### pathInput > `readonly` **pathInput**: `Currency` Defined in: [entities/route.ts:17](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L17) *** ### pathOutput > `readonly` **pathOutput**: `Currency` Defined in: [entities/route.ts:18](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L18) *** ### pools > `readonly` **pools**: [`Pool`](Pool.md)[] Defined in: [entities/route.ts:13](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L13) ## Accessors ### chainId #### Get Signature > **get** **chainId**(): `number` Defined in: [entities/route.ts:58](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L58) ##### Returns `number` *** ### midPrice #### Get Signature > **get** **midPrice**(): `Price`\<`TInput`, `TOutput`\> Defined in: [entities/route.ts:65](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/route.ts#L65) Returns the mid price of the route ##### Returns `Price`\<`TInput`, `TOutput`\> --- ## Trade [@uniswap/v4-sdk](../overview.md) / Trade Defined in: [entities/trade.ts:66](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L66) Represents a trade executed against a set of routes where some percentage of the input is split across each route. Each route has its own set of pools. Pools can not be re-used across routes. Does not account for slippage, i.e., changes in price environment that can occur between the time the trade is submitted and when it is executed. ## Type Parameters | Type Parameter | Description | | ------ | ------ | | `TInput` *extends* `Currency` | The input currency, either Ether or an ERC-20 | | `TOutput` *extends* `Currency` | The output currency, either Ether or an ERC-20 | | `TTradeType` *extends* `TradeType` | The trade type, either exact input or exact output | ## Properties ### swaps > `readonly` **swaps**: `object`[] Defined in: [entities/trade.ts:83](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L83) The swaps of the trade, i.e. which routes and how much is swapped in each that make up the trade. #### inputAmount > **inputAmount**: `CurrencyAmount`\<`TInput`\> #### outputAmount > **outputAmount**: `CurrencyAmount`\<`TOutput`\> #### route > **route**: [`Route`](Route.md)\<`TInput`, `TOutput`\> *** ### tradeType > `readonly` **tradeType**: `TTradeType` Defined in: [entities/trade.ts:92](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L92) The type of the trade, either exact in or exact out. ## Accessors ### executionPrice #### Get Signature > **get** **executionPrice**(): `Price`\<`TInput`, `TOutput`\> Defined in: [entities/trade.ts:149](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L149) The price expressed in terms of output amount/input amount. ##### Returns `Price`\<`TInput`, `TOutput`\> *** ### inputAmount #### Get Signature > **get** **inputAmount**(): `CurrencyAmount`\<`TInput`\> Defined in: [entities/trade.ts:103](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L103) The input amount for the trade assuming no slippage. ##### Returns `CurrencyAmount`\<`TInput`\> *** ### outputAmount #### Get Signature > **get** **outputAmount**(): `CurrencyAmount`\<`TOutput`\> Defined in: [entities/trade.ts:126](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L126) The output amount for the trade assuming no slippage. ##### Returns `CurrencyAmount`\<`TOutput`\> *** ### priceImpact #### Get Signature > **get** **priceImpact**(): `Percent` Defined in: [entities/trade.ts:170](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L170) Returns the percent difference between the route's mid price and the price impact ##### Returns `Percent` *** ### route #### Get Signature > **get** **route**(): [`Route`](Route.md)\<`TInput`, `TOutput`\> Defined in: [entities/trade.ts:74](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L74) ##### Deprecated Deprecated in favor of 'swaps' property. If the trade consists of multiple routes this will return an error. When the trade consists of just a single route, this returns the route of the trade, i.e. which pools the trade goes through. ##### Returns [`Route`](Route.md)\<`TInput`, `TOutput`\> ## Methods ### bestTradeExactIn() > `static` **bestTradeExactIn**\<`TInput`, `TOutput`\>(`pools`, `currencyAmountIn`, `currencyOut`, `__namedParameters`, `currentPools`, `nextAmountIn`, `bestTrades`): `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_INPUT`\>[]> Defined in: [entities/trade.ts:454](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L454) Given a list of pools, and a fixed amount in, returns the top `maxNumResults` trades that go from an input currency amount to an output currency, making at most `maxHops` hops. Note this does not consider aggregation, as routes are linear. It's possible a better route exists by splitting the amount in among multiple routes. #### Type Parameters | Type Parameter | | ------ | | `TInput` *extends* `Currency` | | `TOutput` *extends* `Currency` | #### Parameters | Parameter | Type | Default value | Description | | ------ | ------ | ------ | ------ | | `pools` | [`Pool`](Pool.md)[] | `undefined` | the pools to consider in finding the best trade | | `currencyAmountIn` | `CurrencyAmount`\<`TInput`\> | `undefined` | used in recursion; the original value of the currencyAmountIn parameter | | `currencyOut` | `TOutput` | `undefined` | the desired currency out | | `__namedParameters` | [`BestTradeOptions`](../interfaces/BestTradeOptions.md) | `{}` | - | | `currentPools` | [`Pool`](Pool.md)[] | `[]` | used in recursion; the current list of pools | | `nextAmountIn` | `CurrencyAmount`\<`Currency`\> | `currencyAmountIn` | exact amount of input currency to spend | | `bestTrades` | [`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_INPUT`\>[] | `[]` | used in recursion; the current list of best trades | #### Returns `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_INPUT`\>[]> The exact in trade *** ### bestTradeExactOut() > `static` **bestTradeExactOut**\<`TInput`, `TOutput`\>(`pools`, `currencyIn`, `currencyAmountOut`, `__namedParameters`, `currentPools`, `nextAmountOut`, `bestTrades`): `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_OUTPUT`\>[]> Defined in: [entities/trade.ts:533](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L533) similar to the above method but instead targets a fixed output amount given a list of pools, and a fixed amount out, returns the top `maxNumResults` trades that go from an input currency to an output currency amount, making at most `maxHops` hops note this does not consider aggregation, as routes are linear. it's possible a better route exists by splitting the amount in among multiple routes. #### Type Parameters | Type Parameter | | ------ | | `TInput` *extends* `Currency` | | `TOutput` *extends* `Currency` | #### Parameters | Parameter | Type | Default value | Description | | ------ | ------ | ------ | ------ | | `pools` | [`Pool`](Pool.md)[] | `undefined` | the pools to consider in finding the best trade | | `currencyIn` | `TInput` | `undefined` | the currency to spend | | `currencyAmountOut` | `CurrencyAmount`\<`TOutput`\> | `undefined` | the desired currency amount out | | `__namedParameters` | [`BestTradeOptions`](../interfaces/BestTradeOptions.md) | `{}` | - | | `currentPools` | [`Pool`](Pool.md)[] | `[]` | used in recursion; the current list of pools | | `nextAmountOut` | `CurrencyAmount`\<`Currency`\> | `currencyAmountOut` | the exact amount of currency out | | `bestTrades` | [`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_OUTPUT`\>[] | `[]` | used in recursion; the current list of best trades | #### Returns `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_OUTPUT`\>[]> The exact out trade *** ### createUncheckedTrade() > `static` **createUncheckedTrade**\<`TInput`, `TOutput`, `TTradeType`\>(`constructorArguments`): [`Trade`](Trade.md)\<`TInput`, `TOutput`, `TTradeType`\> Defined in: [entities/trade.ts:305](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L305) Creates a trade without computing the result of swapping through the route. Useful when you have simulated the trade elsewhere and do not have any tick data #### Type Parameters | Type Parameter | Description | | ------ | ------ | | `TInput` *extends* `Currency` | The input currency, either Ether or an ERC-20 | | `TOutput` *extends* `Currency` | The output currency, either Ether or an ERC-20 | | `TTradeType` *extends* `TradeType` | The type of the trade, either exact in or exact out | #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `constructorArguments` | \{ `inputAmount`: `CurrencyAmount`\<`TInput`\>; `outputAmount`: `CurrencyAmount`\<`TOutput`\>; `route`: [`Route`](Route.md)\<`TInput`, `TOutput`\>; `tradeType`: `TTradeType`; \} | The arguments passed to the trade constructor | | `constructorArguments.inputAmount` | `CurrencyAmount`\<`TInput`\> | - | | `constructorArguments.outputAmount` | `CurrencyAmount`\<`TOutput`\> | - | | `constructorArguments.route` | [`Route`](Route.md)\<`TInput`, `TOutput`\> | - | | `constructorArguments.tradeType` | `TTradeType` | - | #### Returns [`Trade`](Trade.md)\<`TInput`, `TOutput`, `TTradeType`\> The unchecked trade *** ### createUncheckedTradeWithMultipleRoutes() > `static` **createUncheckedTradeWithMultipleRoutes**\<`TInput`, `TOutput`, `TTradeType`\>(`constructorArguments`): [`Trade`](Trade.md)\<`TInput`, `TOutput`, `TTradeType`\> Defined in: [entities/trade.ts:336](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L336) Creates a trade without computing the result of swapping through the routes. Useful when you have simulated the trade elsewhere and do not have any tick data #### Type Parameters | Type Parameter | Description | | ------ | ------ | | `TInput` *extends* `Currency` | The input currency, either Ether or an ERC-20 | | `TOutput` *extends* `Currency` | The output currency, either Ether or an ERC-20 | | `TTradeType` *extends* `TradeType` | The type of the trade, either exact in or exact out | #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `constructorArguments` | \{ `routes`: `object`[]; `tradeType`: `TTradeType`; \} | The arguments passed to the trade constructor | | `constructorArguments.routes` | `object`[] | - | | `constructorArguments.tradeType` | `TTradeType` | - | #### Returns [`Trade`](Trade.md)\<`TInput`, `TOutput`, `TTradeType`\> The unchecked trade *** ### exactIn() > `static` **exactIn**\<`TInput`, `TOutput`\>(`route`, `amountIn`): `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_INPUT`\>> Defined in: [entities/trade.ts:195](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L195) Constructs an exact in trade with the given amount in and route #### Type Parameters | Type Parameter | Description | | ------ | ------ | | `TInput` *extends* `Currency` | The input currency, either Ether or an ERC-20 | | `TOutput` *extends* `Currency` | The output currency, either Ether or an ERC-20 | #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `route` | [`Route`](Route.md)\<`TInput`, `TOutput`\> | The route of the exact in trade | | `amountIn` | `CurrencyAmount`\<`TInput`\> | The amount being passed in | #### Returns `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_INPUT`\>> The exact in trade *** ### exactOut() > `static` **exactOut**\<`TInput`, `TOutput`\>(`route`, `amountOut`): `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_OUTPUT`\>> Defined in: [entities/trade.ts:210](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L210) Constructs an exact out trade with the given amount out and route #### Type Parameters | Type Parameter | Description | | ------ | ------ | | `TInput` *extends* `Currency` | The input currency, either Ether or an ERC-20 | | `TOutput` *extends* `Currency` | The output currency, either Ether or an ERC-20 | #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `route` | [`Route`](Route.md)\<`TInput`, `TOutput`\> | The route of the exact out trade | | `amountOut` | `CurrencyAmount`\<`TOutput`\> | The amount returned by the trade | #### Returns `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `EXACT_OUTPUT`\>> The exact out trade *** ### fromRoute() > `static` **fromRoute**\<`TInput`, `TOutput`, `TTradeType`\>(`route`, `amount`, `tradeType`): `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `TTradeType`\>> Defined in: [entities/trade.ts:227](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L227) Constructs a trade by simulating swaps through the given route #### Type Parameters | Type Parameter | Description | | ------ | ------ | | `TInput` *extends* `Currency` | The input currency, either Ether or an ERC-20. | | `TOutput` *extends* `Currency` | The output currency, either Ether or an ERC-20. | | `TTradeType` *extends* `TradeType` | The type of the trade, either exact in or exact out. | #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `route` | [`Route`](Route.md)\<`TInput`, `TOutput`\> | route to swap through | | `amount` | `TTradeType` *extends* `EXACT_INPUT` ? `CurrencyAmount`\<`TInput`\> : `CurrencyAmount`\<`TOutput`\> | the amount specified, either input or output, depending on tradeType | | `tradeType` | `TTradeType` | whether the trade is an exact input or exact output swap | #### Returns `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `TTradeType`\>> The route *** ### fromRoutes() > `static` **fromRoutes**\<`TInput`, `TOutput`, `TTradeType`\>(`routes`, `tradeType`): `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `TTradeType`\>> Defined in: [entities/trade.ts:272](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L272) Constructs a trade from routes by simulating swaps #### Type Parameters | Type Parameter | Description | | ------ | ------ | | `TInput` *extends* `Currency` | The input currency, either Ether or an ERC-20. | | `TOutput` *extends* `Currency` | The output currency, either Ether or an ERC-20. | | `TTradeType` *extends* `TradeType` | The type of the trade, either exact in or exact out. | #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `routes` | `object`[] | the routes to swap through and how much of the amount should be routed through each | | `tradeType` | `TTradeType` | whether the trade is an exact input or exact output swap | #### Returns `Promise`\<[`Trade`](Trade.md)\<`TInput`, `TOutput`, `TTradeType`\>> The trade *** ### maximumAmountIn() > **maximumAmountIn**(`slippageTolerance`, `amountIn`): `CurrencyAmount`\<`TInput`\> Defined in: [entities/trade.ts:415](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L415) Get the maximum amount in that can be spent via this trade for the given slippage tolerance #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `slippageTolerance` | `Percent` | The tolerance of unfavorable slippage from the execution price of this trade | | `amountIn` | `CurrencyAmount`\<`TInput`\> | - | #### Returns `CurrencyAmount`\<`TInput`\> The amount in *** ### minimumAmountOut() > **minimumAmountOut**(`slippageTolerance`, `amountOut`): `CurrencyAmount`\<`TOutput`\> Defined in: [entities/trade.ts:397](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L397) Get the minimum amount that must be received from this trade for the given slippage tolerance #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `slippageTolerance` | `Percent` | The tolerance of unfavorable slippage from the execution price of this trade | | `amountOut` | `CurrencyAmount`\<`TOutput`\> | - | #### Returns `CurrencyAmount`\<`TOutput`\> The amount out *** ### worstExecutionPrice() > **worstExecutionPrice**(`slippageTolerance`): `Price`\<`TInput`, `TOutput`\> Defined in: [entities/trade.ts:430](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L430) Return the execution price after accounting for slippage tolerance #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `slippageTolerance` | `Percent` | the allowed tolerated slippage | #### Returns `Price`\<`TInput`, `TOutput`\> The execution price --- ## V4BaseActionsParser [@uniswap/v4-sdk](../overview.md) / V4BaseActionsParser Defined in: [utils/v4BaseActionsParser.ts:52](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4BaseActionsParser.ts#L52) ## Constructors ### new V4BaseActionsParser() > **new V4BaseActionsParser**(): [`V4BaseActionsParser`](V4BaseActionsParser.md) #### Returns [`V4BaseActionsParser`](V4BaseActionsParser.md) ## Methods ### parseCalldata() > `static` **parseCalldata**(`calldata`): [`V4RouterCall`](../overview.md#v4routercall) Defined in: [utils/v4BaseActionsParser.ts:53](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4BaseActionsParser.ts#L53) #### Parameters | Parameter | Type | | ------ | ------ | | `calldata` | `string` | #### Returns [`V4RouterCall`](../overview.md#v4routercall) --- ## V4Planner [@uniswap/v4-sdk](../overview.md) / V4Planner Defined in: [utils/v4Planner.ts:167](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L167) ## Extended by - [`V4PositionPlanner`](V4PositionPlanner.md) ## Constructors ### new V4Planner() > **new V4Planner**(): [`V4Planner`](V4Planner.md) Defined in: [utils/v4Planner.ts:171](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L171) #### Returns [`V4Planner`](V4Planner.md) ## Properties ### actions > **actions**: `string` Defined in: [utils/v4Planner.ts:168](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L168) *** ### params > **params**: `string`[] Defined in: [utils/v4Planner.ts:169](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L169) ## Methods ### addAction() > **addAction**(`type`, `parameters`): [`V4Planner`](V4Planner.md) Defined in: [utils/v4Planner.ts:176](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L176) #### Parameters | Parameter | Type | | ------ | ------ | | `type` | [`Actions`](../enumerations/Actions.md) | | `parameters` | `any`[] | #### Returns [`V4Planner`](V4Planner.md) *** ### addSettle() > **addSettle**(`currency`, `payerIsUser`, `amount`?): [`V4Planner`](V4Planner.md) Defined in: [utils/v4Planner.ts:213](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L213) #### Parameters | Parameter | Type | | ------ | ------ | | `currency` | `Currency` | | `payerIsUser` | `boolean` | | `amount`? | `BigNumber` | #### Returns [`V4Planner`](V4Planner.md) *** ### addTake() > **addTake**(`currency`, `recipient`, `amount`?): [`V4Planner`](V4Planner.md) Defined in: [utils/v4Planner.ts:218](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L218) #### Parameters | Parameter | Type | | ------ | ------ | | `currency` | `Currency` | | `recipient` | `string` | | `amount`? | `BigNumber` | #### Returns [`V4Planner`](V4Planner.md) *** ### addTrade() > **addTrade**(`trade`, `slippageTolerance`?): [`V4Planner`](V4Planner.md) Defined in: [utils/v4Planner.ts:183](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L183) #### Parameters | Parameter | Type | | ------ | ------ | | `trade` | [`Trade`](Trade.md)\<`Currency`, `Currency`, `TradeType`\> | | `slippageTolerance`? | `Percent` | #### Returns [`V4Planner`](V4Planner.md) *** ### finalize() > **finalize**(): `string` Defined in: [utils/v4Planner.ts:224](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L224) #### Returns `string` --- ## V4PositionManager [@uniswap/v4-sdk](../overview.md) / V4PositionManager Defined in: [PositionManager.ts:206](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L206) ## Properties ### INTERFACE > `static` **INTERFACE**: `Interface` Defined in: [PositionManager.ts:207](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L207) ## Methods ### addCallParameters() > `static` **addCallParameters**(`position`, `options`): [`MethodParameters`](../interfaces/MethodParameters.md) Defined in: [PositionManager.ts:224](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L224) #### Parameters | Parameter | Type | | ------ | ------ | | `position` | [`Position`](Position.md) | | `options` | [`AddLiquidityOptions`](../overview.md#addliquidityoptions) | #### Returns [`MethodParameters`](../interfaces/MethodParameters.md) *** ### collectCallParameters() > `static` **collectCallParameters**(`position`, `options`): [`MethodParameters`](../interfaces/MethodParameters.md) Defined in: [PositionManager.ts:387](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L387) Produces the calldata for collecting fees from a position #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `position` | [`Position`](Position.md) | The position to collect fees from | | `options` | [`CollectOptions`](../overview.md#collectoptions) | Additional information necessary for generating the calldata | #### Returns [`MethodParameters`](../interfaces/MethodParameters.md) The call parameters *** ### createCallParameters() > `static` **createCallParameters**(`poolKey`, `sqrtPriceX96`): [`MethodParameters`](../interfaces/MethodParameters.md) Defined in: [PositionManager.ts:217](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L217) Public methods to encode method parameters for different actions on the PositionManager contract #### Parameters | Parameter | Type | | ------ | ------ | | `poolKey` | [`PoolKey`](../overview.md#poolkey) | | `sqrtPriceX96` | `BigintIsh` | #### Returns [`MethodParameters`](../interfaces/MethodParameters.md) *** ### encodeERC721Permit() > `static` **encodeERC721Permit**(`spender`, `tokenId`, `deadline`, `nonce`, `signature`): `string` Defined in: [PositionManager.ts:435](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L435) #### Parameters | Parameter | Type | | ------ | ------ | | `spender` | `string` | | `tokenId` | `BigintIsh` | | `deadline` | `BigintIsh` | | `nonce` | `BigintIsh` | | `signature` | `string` | #### Returns `string` *** ### encodeModifyLiquidities() > `static` **encodeModifyLiquidities**(`unlockData`, `deadline`): `string` Defined in: [PositionManager.ts:421](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L421) #### Parameters | Parameter | Type | | ------ | ------ | | `unlockData` | `string` | | `deadline` | `BigintIsh` | #### Returns `string` *** ### encodePermitBatch() > `static` **encodePermitBatch**(`owner`, `permitBatch`, `signature`): `string` Defined in: [PositionManager.ts:426](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L426) #### Parameters | Parameter | Type | | ------ | ------ | | `owner` | `string` | | `permitBatch` | [`AllowanceTransferPermitBatch`](../interfaces/AllowanceTransferPermitBatch.md) | | `signature` | `string` | #### Returns `string` *** ### getPermitData() > `static` **getPermitData**(`permit`, `positionManagerAddress`, `chainId`): [`NFTPermitData`](../interfaces/NFTPermitData.md) Defined in: [PositionManager.ts:452](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L452) #### Parameters | Parameter | Type | | ------ | ------ | | `permit` | [`NFTPermitValues`](../interfaces/NFTPermitValues.md) | | `positionManagerAddress` | `string` | | `chainId` | `number` | #### Returns [`NFTPermitData`](../interfaces/NFTPermitData.md) *** ### removeCallParameters() > `static` **removeCallParameters**(`position`, `options`): [`MethodParameters`](../interfaces/MethodParameters.md) Defined in: [PositionManager.ts:314](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L314) Produces the calldata for completely or partially exiting a position #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `position` | [`Position`](Position.md) | The position to exit | | `options` | [`RemoveLiquidityOptions`](../overview.md#removeliquidityoptions) | Additional information necessary for generating the calldata | #### Returns [`MethodParameters`](../interfaces/MethodParameters.md) The call parameters --- ## V4PositionPlanner [@uniswap/v4-sdk](../overview.md) / V4PositionPlanner Defined in: [utils/v4PositionPlanner.ts:8](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4PositionPlanner.ts#L8) ## Extends - [`V4Planner`](V4Planner.md) ## Constructors ### new V4PositionPlanner() > **new V4PositionPlanner**(): [`V4PositionPlanner`](V4PositionPlanner.md) Defined in: [utils/v4Planner.ts:171](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L171) #### Returns [`V4PositionPlanner`](V4PositionPlanner.md) #### Inherited from [`V4Planner`](V4Planner.md).[`constructor`](V4Planner.md#constructors) ## Properties ### actions > **actions**: `string` Defined in: [utils/v4Planner.ts:168](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L168) #### Inherited from [`V4Planner`](V4Planner.md).[`actions`](V4Planner.md#actions) *** ### params > **params**: `string`[] Defined in: [utils/v4Planner.ts:169](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L169) #### Inherited from [`V4Planner`](V4Planner.md).[`params`](V4Planner.md#params) ## Methods ### addAction() > **addAction**(`type`, `parameters`): [`V4Planner`](V4Planner.md) Defined in: [utils/v4Planner.ts:176](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L176) #### Parameters | Parameter | Type | | ------ | ------ | | `type` | [`Actions`](../enumerations/Actions.md) | | `parameters` | `any`[] | #### Returns [`V4Planner`](V4Planner.md) #### Inherited from [`V4Planner`](V4Planner.md).[`addAction`](V4Planner.md#addaction) *** ### addBurn() > **addBurn**(`tokenId`, `amount0Min`, `amount1Min`, `hookData`): `void` Defined in: [utils/v4PositionPlanner.ts:58](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4PositionPlanner.ts#L58) #### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `tokenId` | `BigintIsh` | `undefined` | | `amount0Min` | `BigintIsh` | `undefined` | | `amount1Min` | `BigintIsh` | `undefined` | | `hookData` | `string` | `EMPTY_BYTES` | #### Returns `void` *** ### addDecrease() > **addDecrease**(`tokenId`, `liquidity`, `amount0Min`, `amount1Min`, `hookData`): `void` Defined in: [utils/v4PositionPlanner.ts:46](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4PositionPlanner.ts#L46) #### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `tokenId` | `BigintIsh` | `undefined` | | `liquidity` | `BigintIsh` | `undefined` | | `amount0Min` | `BigintIsh` | `undefined` | | `amount1Min` | `BigintIsh` | `undefined` | | `hookData` | `string` | `EMPTY_BYTES` | #### Returns `void` *** ### addIncrease() > **addIncrease**(`tokenId`, `liquidity`, `amount0Max`, `amount1Max`, `hookData`): `void` Defined in: [utils/v4PositionPlanner.ts:34](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4PositionPlanner.ts#L34) #### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `tokenId` | `BigintIsh` | `undefined` | | `liquidity` | `BigintIsh` | `undefined` | | `amount0Max` | `BigintIsh` | `undefined` | | `amount1Max` | `BigintIsh` | `undefined` | | `hookData` | `string` | `EMPTY_BYTES` | #### Returns `void` *** ### addMint() > **addMint**(`pool`, `tickLower`, `tickUpper`, `liquidity`, `amount0Max`, `amount1Max`, `owner`, `hookData`): `void` Defined in: [utils/v4PositionPlanner.ts:10](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4PositionPlanner.ts#L10) #### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `pool` | [`Pool`](Pool.md) | `undefined` | | `tickLower` | `number` | `undefined` | | `tickUpper` | `number` | `undefined` | | `liquidity` | `BigintIsh` | `undefined` | | `amount0Max` | `BigintIsh` | `undefined` | | `amount1Max` | `BigintIsh` | `undefined` | | `owner` | `string` | `undefined` | | `hookData` | `string` | `EMPTY_BYTES` | #### Returns `void` *** ### addSettle() > **addSettle**(`currency`, `payerIsUser`, `amount`?): [`V4Planner`](V4Planner.md) Defined in: [utils/v4Planner.ts:213](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L213) #### Parameters | Parameter | Type | | ------ | ------ | | `currency` | `Currency` | | `payerIsUser` | `boolean` | | `amount`? | `BigNumber` | #### Returns [`V4Planner`](V4Planner.md) #### Inherited from [`V4Planner`](V4Planner.md).[`addSettle`](V4Planner.md#addsettle) *** ### addSettlePair() > **addSettlePair**(`currency0`, `currency1`): `void` Defined in: [utils/v4PositionPlanner.ts:64](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4PositionPlanner.ts#L64) #### Parameters | Parameter | Type | | ------ | ------ | | `currency0` | `Currency` | | `currency1` | `Currency` | #### Returns `void` *** ### addSweep() > **addSweep**(`currency`, `to`): `void` Defined in: [utils/v4PositionPlanner.ts:76](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4PositionPlanner.ts#L76) #### Parameters | Parameter | Type | | ------ | ------ | | `currency` | `Currency` | | `to` | `string` | #### Returns `void` *** ### addTake() > **addTake**(`currency`, `recipient`, `amount`?): [`V4Planner`](V4Planner.md) Defined in: [utils/v4Planner.ts:218](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L218) #### Parameters | Parameter | Type | | ------ | ------ | | `currency` | `Currency` | | `recipient` | `string` | | `amount`? | `BigNumber` | #### Returns [`V4Planner`](V4Planner.md) #### Inherited from [`V4Planner`](V4Planner.md).[`addTake`](V4Planner.md#addtake) *** ### addTakePair() > **addTakePair**(`currency0`, `currency1`, `recipient`): `void` Defined in: [utils/v4PositionPlanner.ts:70](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4PositionPlanner.ts#L70) #### Parameters | Parameter | Type | | ------ | ------ | | `currency0` | `Currency` | | `currency1` | `Currency` | | `recipient` | `string` | #### Returns `void` *** ### addTrade() > **addTrade**(`trade`, `slippageTolerance`?): [`V4Planner`](V4Planner.md) Defined in: [utils/v4Planner.ts:183](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L183) #### Parameters | Parameter | Type | | ------ | ------ | | `trade` | [`Trade`](Trade.md)\<`Currency`, `Currency`, `TradeType`\> | | `slippageTolerance`? | `Percent` | #### Returns [`V4Planner`](V4Planner.md) #### Inherited from [`V4Planner`](V4Planner.md).[`addTrade`](V4Planner.md#addtrade) *** ### finalize() > **finalize**(): `string` Defined in: [utils/v4Planner.ts:224](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L224) #### Returns `string` #### Inherited from [`V4Planner`](V4Planner.md).[`finalize`](V4Planner.md#finalize) --- ## Actions(Enumerations) [@uniswap/v4-sdk](../overview.md) / Actions Defined in: [utils/v4Planner.ts:14](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L14) Actions ## Description Constants that define what action to perform Not all actions are supported yet. ## Enumeration Members ### BURN_POSITION > **BURN_POSITION**: `3` Defined in: [utils/v4Planner.ts:20](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L20) *** ### CLOSE_CURRENCY > **CLOSE_CURRENCY**: `18` Defined in: [utils/v4Planner.ts:43](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L43) *** ### DECREASE_LIQUIDITY > **DECREASE_LIQUIDITY**: `1` Defined in: [utils/v4Planner.ts:18](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L18) *** ### INCREASE_LIQUIDITY > **INCREASE_LIQUIDITY**: `0` Defined in: [utils/v4Planner.ts:17](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L17) *** ### MINT_POSITION > **MINT_POSITION**: `2` Defined in: [utils/v4Planner.ts:19](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L19) *** ### SETTLE > **SETTLE**: `11` Defined in: [utils/v4Planner.ts:34](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L34) *** ### SETTLE_ALL > **SETTLE_ALL**: `12` Defined in: [utils/v4Planner.ts:35](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L35) *** ### SETTLE_PAIR > **SETTLE_PAIR**: `13` Defined in: [utils/v4Planner.ts:36](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L36) *** ### SWAP_EXACT_IN > **SWAP_EXACT_IN**: `7` Defined in: [utils/v4Planner.ts:28](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L28) *** ### SWAP_EXACT_IN_SINGLE > **SWAP_EXACT_IN_SINGLE**: `6` Defined in: [utils/v4Planner.ts:27](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L27) *** ### SWAP_EXACT_OUT > **SWAP_EXACT_OUT**: `9` Defined in: [utils/v4Planner.ts:30](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L30) *** ### SWAP_EXACT_OUT_SINGLE > **SWAP_EXACT_OUT_SINGLE**: `8` Defined in: [utils/v4Planner.ts:29](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L29) *** ### SWEEP > **SWEEP**: `20` Defined in: [utils/v4Planner.ts:45](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L45) *** ### TAKE > **TAKE**: `14` Defined in: [utils/v4Planner.ts:38](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L38) *** ### TAKE_ALL > **TAKE_ALL**: `15` Defined in: [utils/v4Planner.ts:39](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L39) *** ### TAKE_PAIR > **TAKE_PAIR**: `17` Defined in: [utils/v4Planner.ts:41](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L41) *** ### TAKE_PORTION > **TAKE_PORTION**: `16` Defined in: [utils/v4Planner.ts:40](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L40) --- ## HookOptions [@uniswap/v4-sdk](../overview.md) / HookOptions Defined in: [utils/hook.ts:6](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L6) ## Enumeration Members ### AfterAddLiquidity > **AfterAddLiquidity**: `"afterAddLiquidity"` Defined in: [utils/hook.ts:17](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L17) *** ### AfterAddLiquidityReturnsDelta > **AfterAddLiquidityReturnsDelta**: `"afterAddLiquidityReturnsDelta"` Defined in: [utils/hook.ts:8](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L8) *** ### AfterDonate > **AfterDonate**: `"afterDonate"` Defined in: [utils/hook.ts:11](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L11) *** ### AfterInitialize > **AfterInitialize**: `"afterInitialize"` Defined in: [utils/hook.ts:19](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L19) *** ### AfterRemoveLiquidity > **AfterRemoveLiquidity**: `"afterRemoveLiquidity"` Defined in: [utils/hook.ts:15](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L15) *** ### AfterRemoveLiquidityReturnsDelta > **AfterRemoveLiquidityReturnsDelta**: `"afterRemoveLiquidityReturnsDelta"` Defined in: [utils/hook.ts:7](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L7) *** ### AfterSwap > **AfterSwap**: `"afterSwap"` Defined in: [utils/hook.ts:13](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L13) *** ### AfterSwapReturnsDelta > **AfterSwapReturnsDelta**: `"afterSwapReturnsDelta"` Defined in: [utils/hook.ts:9](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L9) *** ### BeforeAddLiquidity > **BeforeAddLiquidity**: `"beforeAddLiquidity"` Defined in: [utils/hook.ts:18](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L18) *** ### BeforeDonate > **BeforeDonate**: `"beforeDonate"` Defined in: [utils/hook.ts:12](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L12) *** ### BeforeInitialize > **BeforeInitialize**: `"beforeInitialize"` Defined in: [utils/hook.ts:20](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L20) *** ### BeforeRemoveLiquidity > **BeforeRemoveLiquidity**: `"beforeRemoveLiquidity"` Defined in: [utils/hook.ts:16](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L16) *** ### BeforeSwap > **BeforeSwap**: `"beforeSwap"` Defined in: [utils/hook.ts:14](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L14) *** ### BeforeSwapReturnsDelta > **BeforeSwapReturnsDelta**: `"beforeSwapReturnsDelta"` Defined in: [utils/hook.ts:10](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L10) --- ## Subparser [@uniswap/v4-sdk](../overview.md) / Subparser Defined in: [utils/v4Planner.ts:52](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L52) ## Enumeration Members ### PoolKey > **PoolKey**: `4` Defined in: [utils/v4Planner.ts:57](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L57) *** ### V4SwapExactIn > **V4SwapExactIn**: `1` Defined in: [utils/v4Planner.ts:54](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L54) *** ### V4SwapExactInSingle > **V4SwapExactInSingle**: `0` Defined in: [utils/v4Planner.ts:53](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L53) *** ### V4SwapExactOut > **V4SwapExactOut**: `3` Defined in: [utils/v4Planner.ts:56](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L56) *** ### V4SwapExactOutSingle > **V4SwapExactOutSingle**: `2` Defined in: [utils/v4Planner.ts:55](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L55) --- ## AllowanceTransferPermitBatch [@uniswap/v4-sdk](../overview.md) / AllowanceTransferPermitBatch Defined in: [PositionManager.ts:146](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L146) ## Properties ### details > **details**: [`PermitDetails`](PermitDetails.md)[] Defined in: [PositionManager.ts:147](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L147) *** ### sigDeadline > **sigDeadline**: `BigintIsh` Defined in: [PositionManager.ts:149](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L149) *** ### spender > **spender**: `string` Defined in: [PositionManager.ts:148](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L148) --- ## AllowanceTransferPermitSingle [@uniswap/v4-sdk](../overview.md) / AllowanceTransferPermitSingle Defined in: [PositionManager.ts:140](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L140) ## Properties ### details > **details**: [`PermitDetails`](PermitDetails.md) Defined in: [PositionManager.ts:141](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L141) *** ### sigDeadline > **sigDeadline**: `BigintIsh` Defined in: [PositionManager.ts:143](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L143) *** ### spender > **spender**: `string` Defined in: [PositionManager.ts:142](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L142) --- ## BatchPermitOptions [@uniswap/v4-sdk](../overview.md) / BatchPermitOptions Defined in: [PositionManager.ts:152](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L152) ## Properties ### owner > **owner**: `string` Defined in: [PositionManager.ts:153](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L153) *** ### permitBatch > **permitBatch**: [`AllowanceTransferPermitBatch`](AllowanceTransferPermitBatch.md) Defined in: [PositionManager.ts:154](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L154) *** ### signature > **signature**: `string` Defined in: [PositionManager.ts:155](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L155) --- ## BestTradeOptions [@uniswap/v4-sdk](../overview.md) / BestTradeOptions Defined in: [entities/trade.ts:47](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L47) ## Properties ### maxHops? > `optional` **maxHops**: `number` Defined in: [entities/trade.ts:51](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L51) *** ### maxNumResults? > `optional` **maxNumResults**: `number` Defined in: [entities/trade.ts:49](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L49) --- ## CollectSpecificOptions [@uniswap/v4-sdk](../overview.md) / CollectSpecificOptions Defined in: [PositionManager.ts:104](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L104) ## Properties ### recipient > **recipient**: `string` Defined in: [PositionManager.ts:113](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L113) The account that should receive the tokens. *** ### tokenId > **tokenId**: `BigintIsh` Defined in: [PositionManager.ts:108](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L108) Indicates the ID of the position to collect for. --- ## CommonAddLiquidityOptions [@uniswap/v4-sdk](../overview.md) / CommonAddLiquidityOptions Defined in: [PositionManager.ts:72](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L72) Options for producing the calldata to add liquidity. ## Properties ### batchPermit? > `optional` **batchPermit**: [`BatchPermitOptions`](BatchPermitOptions.md) Defined in: [PositionManager.ts:81](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L81) The optional permit2 batch permit parameters for spending token0 and token1 *** ### useNative? > `optional` **useNative**: `NativeCurrency` Defined in: [PositionManager.ts:76](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L76) Whether to spend ether. If true, one of the currencies must be the NATIVE currency. --- ## CommonOptions [@uniswap/v4-sdk](../overview.md) / CommonOptions Defined in: [PositionManager.ts:24](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L24) ## Properties ### deadline > **deadline**: `BigintIsh` Defined in: [PositionManager.ts:37](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L37) When the transaction expires, in epoch seconds. *** ### hookData? > `optional` **hookData**: `string` Defined in: [PositionManager.ts:32](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L32) Optional data to pass to hooks *** ### slippageTolerance > **slippageTolerance**: `Percent` Defined in: [PositionManager.ts:28](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L28) How much the pool price is allowed to move from the specified action. --- ## MethodParameters [@uniswap/v4-sdk](../overview.md) / MethodParameters Defined in: [utils/calldata.ts:7](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/calldata.ts#L7) Generated method parameters for executing a call. ## Properties ### calldata > **calldata**: `string` Defined in: [utils/calldata.ts:11](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/calldata.ts#L11) The hex encoded calldata to perform the given operation *** ### value > **value**: `string` Defined in: [utils/calldata.ts:15](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/calldata.ts#L15) The amount of ether (wei) to send in hex. --- ## MintSpecificOptions [@uniswap/v4-sdk](../overview.md) / MintSpecificOptions Defined in: [PositionManager.ts:47](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L47) ## Properties ### createPool? > `optional` **createPool**: `boolean` Defined in: [PositionManager.ts:56](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L56) Creates pool if not initialized before mint. *** ### migrate? > `optional` **migrate**: `boolean` Defined in: [PositionManager.ts:66](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L66) Whether the mint is part of a migration from V3 to V4. *** ### recipient > **recipient**: `string` Defined in: [PositionManager.ts:51](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L51) The account that should receive the minted NFT. *** ### sqrtPriceX96? > `optional` **sqrtPriceX96**: `BigintIsh` Defined in: [PositionManager.ts:61](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L61) Initial price to set on the pool if creating --- ## ModifyPositionSpecificOptions [@uniswap/v4-sdk](../overview.md) / ModifyPositionSpecificOptions Defined in: [PositionManager.ts:40](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L40) ## Properties ### tokenId > **tokenId**: `BigintIsh` Defined in: [PositionManager.ts:44](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L44) Indicates the ID of the position to increase liquidity for. --- ## NFTPermitData [@uniswap/v4-sdk](../overview.md) / NFTPermitData Defined in: [PositionManager.ts:178](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L178) ## Properties ### domain > **domain**: `TypedDataDomain` Defined in: [PositionManager.ts:179](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L179) *** ### types > **types**: `Record`\<`string`, `TypedDataField`[]\> Defined in: [PositionManager.ts:180](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L180) *** ### values > **values**: [`NFTPermitValues`](NFTPermitValues.md) Defined in: [PositionManager.ts:181](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L181) --- ## NFTPermitOptions [@uniswap/v4-sdk](../overview.md) / NFTPermitOptions Defined in: [PositionManager.ts:174](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L174) ## Extends - [`NFTPermitValues`](NFTPermitValues.md) ## Properties ### deadline > **deadline**: `BigintIsh` Defined in: [PositionManager.ts:170](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L170) #### Inherited from [`NFTPermitValues`](NFTPermitValues.md).[`deadline`](NFTPermitValues.md#deadline) *** ### nonce > **nonce**: `BigintIsh` Defined in: [PositionManager.ts:171](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L171) #### Inherited from [`NFTPermitValues`](NFTPermitValues.md).[`nonce`](NFTPermitValues.md#nonce) *** ### signature > **signature**: `string` Defined in: [PositionManager.ts:175](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L175) *** ### spender > **spender**: `string` Defined in: [PositionManager.ts:168](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L168) #### Inherited from [`NFTPermitValues`](NFTPermitValues.md).[`spender`](NFTPermitValues.md#spender) *** ### tokenId > **tokenId**: `BigintIsh` Defined in: [PositionManager.ts:169](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L169) #### Inherited from [`NFTPermitValues`](NFTPermitValues.md).[`tokenId`](NFTPermitValues.md#tokenid) --- ## NFTPermitValues [@uniswap/v4-sdk](../overview.md) / NFTPermitValues Defined in: [PositionManager.ts:167](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L167) ## Extended by - [`NFTPermitOptions`](NFTPermitOptions.md) ## Properties ### deadline > **deadline**: `BigintIsh` Defined in: [PositionManager.ts:170](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L170) *** ### nonce > **nonce**: `BigintIsh` Defined in: [PositionManager.ts:171](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L171) *** ### spender > **spender**: `string` Defined in: [PositionManager.ts:168](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L168) *** ### tokenId > **tokenId**: `BigintIsh` Defined in: [PositionManager.ts:169](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L169) --- ## PermitDetails [@uniswap/v4-sdk](../overview.md) / PermitDetails Defined in: [PositionManager.ts:133](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L133) ## Properties ### amount > **amount**: `BigintIsh` Defined in: [PositionManager.ts:135](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L135) *** ### expiration > **expiration**: `BigintIsh` Defined in: [PositionManager.ts:136](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L136) *** ### nonce > **nonce**: `BigintIsh` Defined in: [PositionManager.ts:137](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L137) *** ### token > **token**: `string` Defined in: [PositionManager.ts:134](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L134) --- ## RemoveLiquiditySpecificOptions [@uniswap/v4-sdk](../overview.md) / RemoveLiquiditySpecificOptions Defined in: [PositionManager.ts:87](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L87) Options for producing the calldata to exit a position. ## Properties ### burnToken? > `optional` **burnToken**: `boolean` Defined in: [PositionManager.ts:96](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L96) Whether the NFT should be burned if the entire position is being exited, by default false. *** ### liquidityPercentage > **liquidityPercentage**: `Percent` Defined in: [PositionManager.ts:91](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L91) The percentage of position liquidity to exit. *** ### permit? > `optional` **permit**: [`NFTPermitOptions`](NFTPermitOptions.md) Defined in: [PositionManager.ts:101](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L101) The optional permit of the token ID being exited, in case the exit transaction is being sent by an account that does not own the NFT --- ## TransferOptions [@uniswap/v4-sdk](../overview.md) / TransferOptions Defined in: [PositionManager.ts:116](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L116) ## Properties ### recipient > **recipient**: `string` Defined in: [PositionManager.ts:125](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L125) The account that should receive the NFT. *** ### sender > **sender**: `string` Defined in: [PositionManager.ts:120](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L120) The account sending the NFT. *** ### tokenId > **tokenId**: `BigintIsh` Defined in: [PositionManager.ts:130](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L130) The id of the token being sent. --- ## Overview(Reference) ## Enumerations - [Actions](enumerations/Actions.md) - [HookOptions](enumerations/HookOptions.md) - [Subparser](enumerations/Subparser.md) ## Classes - [Hook](classes/Hook.md) - [Pool](classes/Pool.md) - [Position](classes/Position.md) - [Route](classes/Route.md) - [Trade](classes/Trade.md) - [V4BaseActionsParser](classes/V4BaseActionsParser.md) - [V4Planner](classes/V4Planner.md) - [V4PositionManager](classes/V4PositionManager.md) - [V4PositionPlanner](classes/V4PositionPlanner.md) ## Interfaces - [AllowanceTransferPermitBatch](interfaces/AllowanceTransferPermitBatch.md) - [AllowanceTransferPermitSingle](interfaces/AllowanceTransferPermitSingle.md) - [BatchPermitOptions](interfaces/BatchPermitOptions.md) - [BestTradeOptions](interfaces/BestTradeOptions.md) - [CollectSpecificOptions](interfaces/CollectSpecificOptions.md) - [CommonAddLiquidityOptions](interfaces/CommonAddLiquidityOptions.md) - [CommonOptions](interfaces/CommonOptions.md) - [MethodParameters](interfaces/MethodParameters.md) - [MintSpecificOptions](interfaces/MintSpecificOptions.md) - [ModifyPositionSpecificOptions](interfaces/ModifyPositionSpecificOptions.md) - [NFTPermitData](interfaces/NFTPermitData.md) - [NFTPermitOptions](interfaces/NFTPermitOptions.md) - [NFTPermitValues](interfaces/NFTPermitValues.md) - [PermitDetails](interfaces/PermitDetails.md) - [RemoveLiquiditySpecificOptions](interfaces/RemoveLiquiditySpecificOptions.md) - [TransferOptions](interfaces/TransferOptions.md) ## Type Aliases ### AddLiquidityOptions > **AddLiquidityOptions**: [`MintOptions`](overview.md#mintoptions) | [`IncreaseLiquidityOptions`](overview.md#increaseliquidityoptions) Defined in: [PositionManager.ts:187](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L187) --- ### CollectOptions > **CollectOptions**: [`CommonOptions`](interfaces/CommonOptions.md) & [`CollectSpecificOptions`](interfaces/CollectSpecificOptions.md) Defined in: [PositionManager.ts:191](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L191) --- ### HookPermissions > **HookPermissions**: `{ [key in HookOptions]: boolean }` Defined in: [utils/hook.ts:4](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L4) --- ### IncreaseLiquidityOptions > **IncreaseLiquidityOptions**: [`CommonOptions`](interfaces/CommonOptions.md) & [`CommonAddLiquidityOptions`](interfaces/CommonAddLiquidityOptions.md) & [`ModifyPositionSpecificOptions`](interfaces/ModifyPositionSpecificOptions.md) Defined in: [PositionManager.ts:185](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L185) --- ### MintOptions > **MintOptions**: [`CommonOptions`](interfaces/CommonOptions.md) & [`CommonAddLiquidityOptions`](interfaces/CommonAddLiquidityOptions.md) & [`MintSpecificOptions`](interfaces/MintSpecificOptions.md) Defined in: [PositionManager.ts:184](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L184) --- ### Param > **Param**: `object` Defined in: [utils/v4BaseActionsParser.ts:6](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4BaseActionsParser.ts#L6) #### Type declaration ##### name > `readonly` **name**: `string` ##### value > `readonly` **value**: `any` --- ### ParamType > **ParamType**: `object` Defined in: [utils/v4Planner.ts:60](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L60) #### Type declaration ##### name > `readonly` **name**: `string` ##### subparser? > `readonly` `optional` **subparser**: [`Subparser`](enumerations/Subparser.md) ##### type > `readonly` **type**: `string` --- ### PathKey > **PathKey**: `object` Defined in: [utils/encodeRouteToPath.ts:5](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/encodeRouteToPath.ts#L5) #### Type declaration ##### fee > **fee**: `number` ##### hookData > **hookData**: `string` ##### hooks > **hooks**: `string` ##### intermediateCurrency > **intermediateCurrency**: `string` ##### tickSpacing > **tickSpacing**: `number` --- ### PoolKey > **PoolKey**: `object` Defined in: [entities/pool.ts:22](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L22) #### Type declaration ##### currency0 > **currency0**: `string` ##### currency1 > **currency1**: `string` ##### fee > **fee**: `number` ##### hooks > **hooks**: `string` ##### tickSpacing > **tickSpacing**: `number` --- ### RemoveLiquidityOptions > **RemoveLiquidityOptions**: [`CommonOptions`](interfaces/CommonOptions.md) & [`RemoveLiquiditySpecificOptions`](interfaces/RemoveLiquiditySpecificOptions.md) & [`ModifyPositionSpecificOptions`](interfaces/ModifyPositionSpecificOptions.md) Defined in: [PositionManager.ts:189](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/PositionManager.ts#L189) --- ### SwapExactIn > **SwapExactIn**: `object` Defined in: [utils/v4BaseActionsParser.ts:29](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4BaseActionsParser.ts#L29) #### Type declaration ##### amountIn > `readonly` **amountIn**: `string` ##### amountOutMinimum > `readonly` **amountOutMinimum**: `string` ##### currencyIn > `readonly` **currencyIn**: `string` ##### path > `readonly` **path**: readonly [`PathKey`](overview.md#pathkey)[] --- ### SwapExactInSingle > **SwapExactInSingle**: `object` Defined in: [utils/v4BaseActionsParser.ts:21](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4BaseActionsParser.ts#L21) #### Type declaration ##### amountIn > `readonly` **amountIn**: `string` ##### amountOutMinimum > `readonly` **amountOutMinimum**: `string` ##### hookData > `readonly` **hookData**: `string` ##### poolKey > `readonly` **poolKey**: [`PoolKey`](overview.md#poolkey) ##### zeroForOne > `readonly` **zeroForOne**: `boolean` --- ### SwapExactOut > **SwapExactOut**: `object` Defined in: [utils/v4BaseActionsParser.ts:44](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4BaseActionsParser.ts#L44) #### Type declaration ##### amountInMaximum > `readonly` **amountInMaximum**: `string` ##### amountOut > `readonly` **amountOut**: `string` ##### currencyOut > `readonly` **currencyOut**: `string` ##### path > `readonly` **path**: readonly [`PathKey`](overview.md#pathkey)[] --- ### SwapExactOutSingle > **SwapExactOutSingle**: `object` Defined in: [utils/v4BaseActionsParser.ts:36](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4BaseActionsParser.ts#L36) #### Type declaration ##### amountInMaximum > `readonly` **amountInMaximum**: `string` ##### amountOut > `readonly` **amountOut**: `string` ##### hookData > `readonly` **hookData**: `string` ##### poolKey > `readonly` **poolKey**: [`PoolKey`](overview.md#poolkey) ##### zeroForOne > `readonly` **zeroForOne**: `boolean` --- ### V4RouterAction > **V4RouterAction**: `object` Defined in: [utils/v4BaseActionsParser.ts:11](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4BaseActionsParser.ts#L11) #### Type declaration ##### actionName > `readonly` **actionName**: `string` ##### actionType > `readonly` **actionType**: [`Actions`](enumerations/Actions.md) ##### params > `readonly` **params**: readonly [`Param`](overview.md#param)[] --- ### V4RouterCall > **V4RouterCall**: `object` Defined in: [utils/v4BaseActionsParser.ts:17](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4BaseActionsParser.ts#L17) #### Type declaration ##### actions > `readonly` **actions**: readonly [`V4RouterAction`](overview.md#v4routeraction)[] ## Variables ### DYNAMIC_FEE_FLAG > `const` **DYNAMIC_FEE_FLAG**: `8388608` = `0x800000` Defined in: [entities/pool.ts:19](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/pool.ts#L19) --- ### hookFlagIndex > `const` **hookFlagIndex**: `object` Defined in: [utils/hook.ts:23](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/hook.ts#L23) #### Type declaration ##### afterAddLiquidity > **afterAddLiquidity**: `number` = `10` ##### afterAddLiquidityReturnsDelta > **afterAddLiquidityReturnsDelta**: `number` = `1` ##### afterDonate > **afterDonate**: `number` = `4` ##### afterInitialize > **afterInitialize**: `number` = `12` ##### afterRemoveLiquidity > **afterRemoveLiquidity**: `number` = `8` ##### afterRemoveLiquidityReturnsDelta > **afterRemoveLiquidityReturnsDelta**: `number` = `0` ##### afterSwap > **afterSwap**: `number` = `6` ##### afterSwapReturnsDelta > **afterSwapReturnsDelta**: `number` = `2` ##### beforeAddLiquidity > **beforeAddLiquidity**: `number` = `11` ##### beforeDonate > **beforeDonate**: `number` = `5` ##### beforeInitialize > **beforeInitialize**: `number` = `13` ##### beforeRemoveLiquidity > **beforeRemoveLiquidity**: `number` = `9` ##### beforeSwap > **beforeSwap**: `number` = `7` ##### beforeSwapReturnsDelta > **beforeSwapReturnsDelta**: `number` = `3` --- ### V4_BASE_ACTIONS_ABI_DEFINITION > `const` **V4_BASE_ACTIONS_ABI_DEFINITION**: `{ [key in Actions]: readonly ParamType[] }` Defined in: [utils/v4Planner.ts:82](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/v4Planner.ts#L82) ## Functions ### amountWithPathCurrency() > **amountWithPathCurrency**(`amount`, `pool`): `CurrencyAmount`\<`Currency`\> Defined in: [utils/pathCurrency.ts:4](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/pathCurrency.ts#L4) #### Parameters | Parameter | Type | | --------- | ---------------------------- | | `amount` | `CurrencyAmount`\<`Currency`\> | | `pool` | [`Pool`](classes/Pool.md) | #### Returns `CurrencyAmount`\<`Currency`\> --- ### encodeRouteToPath() > **encodeRouteToPath**(`route`, `exactOutput`?): [`PathKey`](overview.md#pathkey)[] Defined in: [utils/encodeRouteToPath.ts:13](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/encodeRouteToPath.ts#L13) #### Parameters | Parameter | Type | | -------------- | --------------------------------------------------- | | `route` | [`Route`](classes/Route.md)\<`Currency`, `Currency`\> | | `exactOutput`? | `boolean` | #### Returns [`PathKey`](overview.md#pathkey)[] --- ### getPathCurrency() > **getPathCurrency**(`currency`, `pool`): `Currency` Defined in: [utils/pathCurrency.ts:12](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/pathCurrency.ts#L12) #### Parameters | Parameter | Type | | ---------- | ------------------------- | | `currency` | `Currency` | | `pool` | [`Pool`](classes/Pool.md) | #### Returns `Currency` --- ### priceToClosestTick() > **priceToClosestTick**(`price`): `number` Defined in: [utils/priceTickConversions.ts:35](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/priceTickConversions.ts#L35) Returns the first tick for which the given price is greater than or equal to the tick price #### Parameters | Parameter | Type | Description | | --------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `price` | `Price`\<`Currency`, `Currency`\> | for which to return the closest tick that represents a price less than or equal to the input price, i.e. the price of the returned tick is less than or equal to the input price | #### Returns `number` --- ### sortsBefore() > **sortsBefore**(`currencyA`, `currencyB`): `boolean` Defined in: [utils/sortsBefore.ts:3](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/sortsBefore.ts#L3) #### Parameters | Parameter | Type | | ----------- | ---------- | | `currencyA` | `Currency` | | `currencyB` | `Currency` | #### Returns `boolean` --- ### tickToPrice() > **tickToPrice**(`baseCurrency`, `quoteCurrency`, `tick`): `Price`\<`Currency`, `Currency`\> Defined in: [utils/priceTickConversions.ts:20](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/priceTickConversions.ts#L20) Returns a price object corresponding to the input tick and the base/quote token Inputs must be tokens because the address order is used to interpret the price represented by the tick #### Parameters | Parameter | Type | Description | | --------------- | ---------- | -------------------------------------- | | `baseCurrency` | `Currency` | - | | `quoteCurrency` | `Currency` | - | | `tick` | `number` | the tick for which to return the price | #### Returns `Price`\<`Currency`, `Currency`\> --- ### toAddress() > **toAddress**(`currency`): `string` Defined in: [utils/currencyMap.ts:7](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/currencyMap.ts#L7) #### Parameters | Parameter | Type | | ---------- | ---------- | | `currency` | `Currency` | #### Returns `string` --- ### toHex() > **toHex**(`bigintIsh`): `string` Defined in: [utils/calldata.ts:23](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/utils/calldata.ts#L23) Converts a big int to a hex string #### Parameters | Parameter | Type | Description | | ----------- | ----------- | ----------- | | `bigintIsh` | `BigintIsh` | | #### Returns `string` The hex encoded calldata --- ### tradeComparator() > **tradeComparator**\<`TInput`, `TOutput`, `TTradeType`\>(`a`, `b`): `number` Defined in: [entities/trade.ts:17](https://github.com/Uniswap/sdks/blob/9cf6edb2df79338ae58f7ea7ca979c35a8a9bd56/sdks/v4-sdk/src/entities/trade.ts#L17) Trades comparator, an extension of the input output comparator that also considers other dimensions of the trade in ranking them #### Type Parameters | Type Parameter | Description | | ---------------------------------- | -------------------------------------------------- | | `TInput` _extends_ `Currency` | The input currency, either Ether or an ERC-20 | | `TOutput` _extends_ `Currency` | The output currency, either Ether or an ERC-20 | | `TTradeType` _extends_ `TradeType` | The trade type, either exact input or exact output | #### Parameters | Parameter | Type | Description | | --------- | -------------------------------------------------------------- | --------------------------- | | `a` | [`Trade`](classes/Trade.md)\<`TInput`, `TOutput`, `TTradeType`\> | The first trade to compare | | `b` | [`Trade`](classes/Trade.md)\<`TInput`, `TOutput`, `TTradeType`\> | The second trade to compare | #### Returns `number` A sorted ordering for two neighboring elements in a trade array