SharkTeam: Best Security Practices for Uniswap V4 Hooks

CN
1 year ago

Recently, Uniswap Lab announced the development progress of the next generation AMM Uniswap V4, and publicly released the whitepaper and code repository. The V4 whitepaper is only 3 pages long, as V4 did not make too many modifications to the core algorithm logic of AMM, but instead added some new features on top of V3 to meet more scenario requirements. SharkTeam will analyze the new features brought by V4 based on the currently open-sourced code, and focus on the important feature Hook introduced by V4 to analyze the best application practices.

I. Differences between V4 and V3

1.1 AMM

At the algorithm level, Uniswap V4 did not modify V3 and still uses the liquidity algorithm based on constant product x*y=k.

In Uniswap V3, each trading pair can have 4 pools (originally 3, later added a new 1bp pool), representing pools with fee rates of 0.01%, 0.05%, 0.3%, 1%. These pools correspond to different tick spaces, and when creating a pool, only one of these 4 types can be selected.

In Uniswap V4, theoretically, each trading pair can have any number of pools, and each pool's fee rate and tick space can also be any value.

This also brings a problem: the liquidity of trading pairs in Uniswap V4 will be fragmented, so a more efficient router/aggregator is needed to help users find the optimal trading path.

1.2 Hooks

Hooks are a set of contracts developed by third parties or Uniswap official, and when creating a pool, the pool can choose to bind a hook. Later, at specific stages of the transaction, the pool will automatically call the Hook contract bound to it. Uniswap V4 defines the stages at which hook contracts can be executed:

  • beforeInitialize

  • afterInitialize

  • beforeModifyPosition

  • afterModifyPosition

  • beforeSwap

  • afterSwap

  • beforeDonate

  • afterDonate

These stages represent the before and after of operations such as initializing the pool, adding/removing liquidity, trading, and donating, and can call hook contracts.

Hook contracts need to explicitly specify at which of the above stages the execution should take place, and the pool needs to know whether the corresponding Hook needs to be executed at a certain stage. To save gas, these flags are not stored in the contract, but need the Hook to use a specific address to indicate. The specific judgment code is as follows:

SharkTeam: Best Security Practices for Uniswap V4 Hooks

SharkTeam: Best Security Practices for Uniswap V4 Hooks

It can be seen that the first 8 bits of the Hook address are used to mark whether the Hook needs to be executed at a specific stage.

Therefore, the developer of the Hook needs to generate an address that meets the requirements of the Pool when deploying the contract, which usually requires using Create2 + calculating a random Salt to achieve.

The following is an example of Hook execution in the whitepaper:

SharkTeam: Best Security Practices for Uniswap V4 Hooks

It can be seen that before and after executing the swap, the pool will first check whether the corresponding flag of the Hook for the pool is enabled, and if it is, it will automatically call the corresponding function of the Hook contract.

1.3 Dynamic fee ratio

In addition to being able to execute code at specific stages, Hooks can also determine the swap fee ratio and withdraw fee ratio for a pool. The withdraw fee ratio refers to the fee that users need to pay to the Hook when removing liquidity. In addition, Hooks can also specify a portion of the swap fee to be collected for themselves.

When creating a pool, the fee parameter (uint24) is used to mark whether this pool uses a dynamic fee, and whether to enable hook swap fee and withdraw fee:

SharkTeam: Best Security Practices for Uniswap V4 Hooks

If dynamic fee is enabled, the pool will call the Hook contract to obtain the current swap fee ratio before each swap. The Hook contract needs to implement the getFee() function to return the current swap fee ratio.

Hooks make Uniswap V4 a developer platform, giving AMM more possibilities. Some functions that can be implemented using Hooks include TWAMM (Time-Weighted Automated Market Maker), Limit Order, LP Reinvestment, etc., which will be detailed in subsequent chapters.

1.4 Singleton Contract

In Uniswap V3, a new contract needs to be deployed every time a new pool is created, which consumes a lot of gas. However, in reality, the code used by these pools is the same, with only different initialization parameters. Uniswap V4 introduces the Singleton contract to manage all pools, so creating a new pool no longer requires deploying a new contract, saving gas for deploying contracts.

In addition, the use of Singleton contracts reduces the need for token transfers during transactions. Since all pools are in the same contract, cross-pool swaps can be directly completed within the contract. In V3, cross-pool swaps would require tokens to be transferred back and forth between different pools, increasing gas costs.

At the same time, in V4, all pools use the same contract, and the internal token accounting in the contract is simplified to account for each token individually, rather than by pool. This makes it more convenient to borrow a large amount of tokens using flash loans.

1.5 extload

To facilitate integration with Hooks and other contracts, the V4 contract has added the extload function, making all internal states of the contract externally readable, and all pool states completely transparent to external parties.

1.6 Flash Accounting

To reduce token transfers in cross-pool swaps, V4 also uses a method called Flash Accounting, which standardizes the process of swap, add/remove liquidity, and flash loans into a process similar to flash loans:

(1) The user obtains a lock

(2) The user performs any operation, such as swapping in multiple pools, adding/removing liquidity, or borrowing tokens from pools through flash loans

(3) All token transfers generated by the user's operations are recorded in the lock

(4) After all operations are completed, the user can withdraw the tokens obtained, while needing to pay the tokens recorded in the lock.

These processes need to occur within a single transaction.

As a result, if a transaction requires swaps across multiple pools, only two transfers are needed for settlement. For example, in a swap like ETH->USDC-BTC, the intermediate token USDC does not require any transfers at all.

1.7 ERC1155 mint/burn

Flash Accounting can reduce token transfers in the same transaction, and by using ERC1155 tokens, it can further reduce token transfers across multiple transactions.

V4 allows the use of ERC1155 mint to store your tokens in the V4 contract, so you can use these tokens in multiple transactions without needing to transfer the tokens to the V4 contract each time.

ERC1155 burn can be used to withdraw the tokens stored in the V4 contract.

ERC1155 is suitable for users who frequently swap or add/remove liquidity, as these users can directly store commonly used tokens in the V4 contract, reducing gas costs for token transfers.

II. Examples of Best Practices for Hooks

2.1 TWAMM (Time-Weighted Automated Market Maker)

Alice wants to purchase $100 million worth of Ether on the blockchain. Executing such a large-scale order on existing Automated Market Maker (AMM) platforms (such as Uniswap) would be very expensive, as these platforms may charge Alice high fees to prevent her from taking advantage of insider information to get a better price.

I have completed the translation of the provided markdown text. Here is the translated text:

In order to obtain a better price, Alice's best option is to manually split the order into several smaller sub-orders and gradually execute them over a few hours. The purpose of this is to give the market enough time to realize that she does not have insider information, thus giving her a better price. However, even if she sends several larger sub-orders, each sub-order will still have a significant impact on the price, and is also susceptible to "sandwich attacks" by hostile traders.

TWAMM solves this problem by representing Alice in the trades. It decomposes her order into an infinite number of tiny virtual orders to ensure smooth execution over time. At the same time, TWAMM uses the special mathematical relationship embedded in the AMM protocol to spread the gas cost across these virtual orders. Since TWAMM processes trades between blocks, it is also less susceptible to "sandwich attacks".

Overall, TWAMM provides Alice with a more efficient way to conduct large-scale trades, avoiding high fees and potential market manipulation.

2.1.1 Principles

TWAMM has a built-in AMM, which is no different from other AMMs. Users can directly trade spot transactions through this AMM and also add liquidity to it. However, TWAMM also has two TWAP order pools, each used to execute TWAP orders in two directions. When a user submits an order, specifying the token input quantity and duration, TWAMM places orders of the same direction into the corresponding pool and automatically executes the trades at the specified trading speed. After the user's order is fully executed, the user can withdraw the tokens obtained. Before the user's order is fully executed, the user can also cancel or modify the order's token quantity.

In Ethereum, smart contracts can only be triggered for execution by EOA addresses, and cannot execute automatically. Therefore, TWAMM needs a keeper account to periodically send transactions to settle the pending token trades in its order pool.

Of course, TWAMM can also automatically settle the order pool each time a user interacts with it, eliminating the cost of a keeper, which is a common way for DeFi protocols to handle streaming data.

2.1.2 Why is this trading model difficult to sandwich attack?

This attack is difficult to implement because the timestamp within a block does not change. Attackers must raise the price of the pool in the last transaction of a block, so that the settlement of TWAMM in the next block will be affected. This requires the sandwich attack to occur across multiple blocks, which undoubtedly poses a significant risk to the attacker, as other arbitrageurs may intervene in the middle, causing the attacker to suffer losses.

Furthermore, due to the presence of arbitrageurs, such price manipulation is destined to be unsustainable. Due to the nature of TWAP orders, it does not trade a large amount of tokens in a short period of time, so in most cases, the losses are limited.

2.1.3 TWAMM Workflow in V4

(1) This Hook maintains two TWAP order pools, representing TWAP orders in two trading directions.

(2) Users can submit TWAP orders through this Hook, specifying the token, quantity, and duration of the trade.

(3) This Hook registers afterSwap, triggering the Hook logic after each swap is completed.

(4) When triggered, the Hook is responsible for settling the two TWAP order pools.

(5) Users can manually trigger settlement at any time.

(6) Users can cancel or modify the token quantity in TWAP orders.

2.1.4 Detailed Example

TWAMM registers three stages to call the logic of hooks, initializes TWAMM after pool initialization, and triggers the hook logic after each user trade or position adjustment.

SharkTeam: Best Security Practices for Uniswap V4 Hooks

Users can manually call the submitOrder function in TWAMM to submit their orders to the contract.

SharkTeam: Best Security Practices for Uniswap V4 Hooks

When users add their orders to the contract, the execution of the order is automatically triggered each time the pool performs a swap or modifyPosition operation.

SharkTeam: Best Security Practices for Uniswap V4 Hooks

Every time a user calls the swap function in V4 to trade or the modifyPosition function to change the position, the execution function in TWAMM is triggered, which calls the internal function _executeTWAMMOrders to continue the execution of previously uncompleted orders.

_executeTWAMMOrders function

SharkTeam: Best Security Practices for Uniswap V4 Hooks

SharkTeam: Best Security Practices for Uniswap V4 Hooks

SharkTeam: Best Security Practices for Uniswap V4 Hooks

The above describes the process of updating orders. After the execution is completed, the current execution time of the TWAMM order is updated.

2.2 Limit Order

Unlike market orders, which are executed immediately at the last market price, limit orders are executed immediately upon reaching the specified price. Most DEXs based on Automated Market Makers (AMMs) default to a market order system, which is simple and easy to understand for newcomers. Market orders are either executed or fail due to parameters such as maximum price impact. In contrast, in limit orders, the order is only executed when the asset price reaches the limit price, otherwise the order remains open.

For example, suppose ETH is currently trading at 1 ETH = 1500 DAI in the ETH/DAI pool. A user can place a take-profit order, specifying "Sell all my ETH if 1 ETH = 2000 DAI". If this price is reached, the user's ETH will be automatically exchanged for DAI in a decentralized manner on-chain.

In previous versions of Uniswap, limit orders were actually not possible. Most AMMs only allow market buys and sells. However, in the V4 version, due to the powerful features and scalability of hooks, the foundation for implementing limit orders now exists.

2.2.1 Principles

The design principle of limit orders is simpler compared to TWAMM, and currently, limit orders related to adding liquidity have been implemented. Implementing limit orders for trading will also be relatively easy.

Due to the existence of tickLower and tickUpper in V4, and changes in the trading situation of the pool, lower and upper will change. When a user wants to add liquidity but does not want to do so at the current price, the limit order hook can be used to execute this requirement. In the hook, the corresponding price is set, and after each swap is completed, the hook checks the current price range and adds liquidity to the pool if the specified price is reached.

2.2.2 Limit Order Workflow in V4

  1. The limit order maintains multiple epochs as limit orders for different lower and trading directions.

  2. Users can submit their price lower and trading direction through this hook to add limit orders to the contract.

  3. This Hook registers afterSwap, triggering the hook logic after each swap is completed.

  4. When triggered, the Hook verifies the current price range and checks if there are any limit orders in the current price range that need to add liquidity.

  5. Users can withdraw or add liquidity at any time.

2.2.3 Detailed Example

The contract registers two stages to trigger the hook, initializes the hook after pool initialization, and triggers the hook logic after each exchange is completed.

SharkTeam: Best Security Practices for Uniswap V4 Hooks

SharkTeam: Best Security Practices for Uniswap V4 Hooks

Users call the place function to specify the quantity, price, and trading direction for adding liquidity, and the hook first adds the desired liquidity to the pool and then creates the corresponding limit order for the user.

SharkTeam: Best Security Practices for Uniswap V4 Hooks

After each swap is completed, the hook is triggered, and based on the current price range, it adds liquidity to the pool for any existing limit orders in the price range.

SharkTeam: Best Security Practices for Uniswap V4 Hooks

SharkTeam: Best Security Practices for Uniswap V4 Hooks

Users can call the kill function before the limit order is completed to cancel the limit order and receive the liquidity added in this transaction.

SharkTeam: Best Security Practices for Uniswap V4 Hooks

Users can call the withdraw function when they want to remove liquidity, and they can withdraw the desired liquidity.

In summary, this limit order hook provides users with a more convenient way to set the price at which they want to add liquidity. When the price range in the pool reaches the specified price, the hook automatically adds liquidity to the pool for the user, and the user can cancel and withdraw liquidity at any time.

In addition to TWAMM and Limit Order, hooks can also be used to implement LP reinvestment, dynamic fee changes, and other functions. Due to space constraints, we will elaborate on these in subsequent analyses:

LP Reinvestment: LP users can use hooks to add, modify, and remove liquidity. Hooks can register afterSwap and afterModifyPosition for invocation. Since users uniformly use hooks to add liquidity, the address for adding liquidity in the poolManager is only the hook. The hook can check the current time each time it is triggered, and after a certain period, choose to withdraw liquidity rewards and add the obtained LP tokens back to the pool to automatically optimize user returns.

Dynamic Fee Changes: Hooks can register interfaces such as beforeSwap to modify dynamic fees before the exchange. Dynamic fees can be quantified based on the number of tick jumps generated by a single swap to measure volatility, and thus dynamically change the fees to hedge against impermanent loss for LPs. This reduces the impact of impermanent loss caused by trading on liquidity providers.

SharkTeam: Best Security Practices for Uniswap V4 Hooks

3. Best Security Practices for Hooks

Minimize the Use of require and revert

In the logic of functions related to pool calls to hooks, try to minimize the use of revert statements. Since the pool contract and the hooks contract have a common relationship, a transaction rollback in the hooks contract will also cause the pool transactions to be rolled back. If there are irrelevant revert statements in the hooks that cause transaction rollback, it may prevent users from using the pool functions normally.

SharkTeam: Best Security Practices for Uniswap V4 Hooks

Avoid Using Self-Destruct Functions

Avoid using the selfdestruct function in hooks. If the self-destruct function is called in the hook, it will not only cause problems in the logic of the hook, but also prevent the pool functions from operating normally, resulting in the loss of assets in the entire pool.

Strict Permission Control

Strictly control the permissions in the hook contract to avoid excessive permissions for roles. Implement multi-signature management for privileged roles to prevent single-point attacks. Avoid situations where privileged roles can arbitrarily modify contract state variables, as this may lead to logic errors causing transaction rollbacks and affecting the normal use of the pool. Follow the principle of least privilege, and use the OpenZeppelin AccessControl contract to implement finer-grained access control, as this practice restricts each system component to follow the principle of least privilege.

Implement Reentrancy Protection

As external extension code for the Pool, hooks should also be aware of potential reentrancy attacks in the contract, such as reentrancy that may occur when transferring native tokens in limit orders, leading to loss of contract assets. Check and attempt to update all states before calling external contracts or the so-called "check-effect-interact" pattern. This way, even if reentrancy occurs, it will not have any impact because all states have been updated.

Contract Upgrade Control

Some developers may use proxy contracts for future changes and upgrades to hook logic, but this also requires attention to potential issues with contract upgrades. First, even in proxy mode, the proxy contract needs to declare the corresponding stages, such as beforeSwap, otherwise the pool will not be able to verify the correct return values. Secondly, before calling delegatecall, check if the target contract exists. Solidity does not perform this check for us, and ignoring it may lead to unexpected behavior and security issues. Carefully consider the order of variable declarations, as it may lead to issues such as variables packing into the same slot, affecting gas costs, memory layout, delegate call results, etc.

About Us

SharkTeam's vision is to comprehensively protect the security of the Web3 world. The team consists of experienced security professionals and senior researchers from around the world, proficient in the underlying theories of blockchain and smart contracts, providing services including smart contract audits, on-chain analysis, and emergency response. The team has established long-term partnerships with key participants in the blockchain ecosystem, such as Polkadot, Moonbeam, Polygon, OKC, Huobi Global, imToken, ChainIDE, and others.

Official Website | Twitter

免责声明:本文章仅代表作者个人观点,不代表本平台的立场和观点。本文章仅供信息分享,不构成对任何人的任何投资建议。用户与作者之间的任何争议,与本平台无关。如网页中刊载的文章或图片涉及侵权,请提供相关的权利证明和身份证明发送邮件到support@aicoin.com,本平台相关工作人员将会进行核查。

ad
出入金首选欧易,注册立返20%
Ad
Share To
APP

X

Telegram

Facebook

Reddit

CopyLink