Risk Warning: Beware of illegal fundraising in the name of 'virtual currency' and 'blockchain'. — Five departments including the Banking and Insurance Regulatory Commission
Information
Discover
Search
Login
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt
BTC
ETH
HTX
SOL
BNB
View Market

SharkTeam: Best Security Practices for Uniswap V4 Hook

星球君的朋友们
Odaily资深作者
2023-08-02 09:51
This article is about 5308 words, reading the full article takes about 8 minutes
Based on the currently open source code, let's take a look at what new features V4 brings?

In recent news, Uniswap Lab has announced the development progress of the next generation AMM, Uniswap V4, and has released the whitepaper and code repository. The whitepaper for V4 is only 3 pages long because V4 didn't make significant changes to the core algorithm of AMM. Instead, it adds some new features on top of V3 to meet more scenario requirements. SharkTeam will analyze the new features introduced by V4 based on the currently open-source code and discuss the best practices for utilizing the important feature, Hooks.

1. Differences between V4 and V3

1.1 AMM

In terms of the AMM algorithm, Uniswap V4 did not make any modifications to V3 and still uses the constant-product liquidity algorithm, x*y=k.

In Uniswap V3, each trading pair can have four pools (originally three, with an additional new 1 bp pool), representing fee rates of 0.01%, 0.05%, 0.3%, and 1%. These pools have different tick spaces. When creating a pool, only one of these four options can be selected.

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

This also brings a challenge: 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 is a set of contracts developed by third parties or Uniswap official. When creating a pool, the pool can choose to bind a hook. After that, at specific stages of the transaction, the pool will automatically call the hook contract that is bound to it. Uniswap V4 defines the following stages where hook contracts can be executed:

  • beforeInitialize

  • afterInitialize

  • beforeModifyPosition

  • afterModifyPosition

  • beforeSwap

  • afterSwap

  • beforeDonate

  • afterDonate

These stages represent the before and after actions of initializing the pool, adding/removing liquidity, swapping, and donating, which can trigger the execution of hook contracts.

Hook contracts need to explicitly specify in which of the above stages they should be executed. Pool, on the other hand, needs to know whether a corresponding hook should be executed at a certain stage. To save gas, these flags are not stored in the contract but instead require the hook to use specific addresses for identification. The code for determining this is as follows:

As can be seen, the first 8 bits of the hook address are used to indicate the flags for whether the hook needs to be executed at specific stages.

Therefore, developers of the hooks need to generate addresses that meet the requirements of the pool when deploying the contracts. This usually requires using Create2 and calculating a random salt.

Here is an example of hook execution from the whitepaper:

You can see that before and after executing swap, the pool will first check whether the corresponding Hook for the pool has enabled the corresponding flag. If enabled, the Hook contract's corresponding functions will be called automatically.

1.3 Dynamic fee ratio

In addition to executing code at specific stages, Hooks can also determine the swap fee rate and withdraw fee rate for a specific pool. The withdraw fee rate refers to the fee that users need to pay to the Hook when they remove 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 (uint 24) needs to be used, with the first 4 bits indicating whether the pool uses dynamic fees and whether to enable hook swap fee and withdraw fee:

If dynamic fees are enabled, the pool will call the Hook contract to get 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 AMMs 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 described in detail in subsequent chapters.

1.4 Singleton contract

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

In addition, the advantage of using the Singleton contract is that it can reduce the token transfers during the transaction process, because all pools are in the same contract, so swaps across pools can be directly done within the contract. In V3, swaps across pools require transferring the token between different pools, which increases gas consumption.

Meanwhile, in V4, all pools use the same contract, and the accounting of tokens within the contract is simplified to account for each token individually instead of accounting for each 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 adds the extload function, which makes all internal states of the contract readable externally, providing full transparency of the pool's status.

1.6 Flash Accounting

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

(1) User obtains a lock.

(2) User performs any operations, such as swapping between multiple pools, adding/removing liquidity, or borrowing tokens from pools through flash loans.

(3) Token transfers generated by all user operations are recorded in the lock.

(4) After all operations are completed, the user can withdraw the token they obtained, while also paying the tokens recorded in the lock.

These processes need to occur within a single transaction.

Therefore, if a transaction involves swapping across multiple pools, only two token transfers are needed for settlement. For example, in a swap like ETH->USDC-BTC, USDC, as an intermediate token, does not require any transfers.

1.7 ERC 1155 mint/burn

Flash Accounting can reduce token transfers for swaps in the same transaction, and by using ERC 1155 tokens, can further reduce token transfers for multiple transactions.

V4 allows you to save your tokens in the V4 contract through ERC 1155 mint, so you can use these tokens in multiple transactions without transferring the tokens to the V4 contract every time.

ERC 1155 burn can be used to withdraw tokens saved in the V4 contract.

ERC 1155 is suitable for users who frequently swap or add/remove liquidity. These users can directly save commonly used tokens in the V4 contract, which can reduce gas costs for token transfers.

2. 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 order on existing Automated Market Maker (AMM) platforms like Uniswap can be very expensive, as these platforms may charge Alice high fees to prevent her from taking advantage of insider information to get better prices.

To get better prices, Alice's best option is to manually split the order into several smaller sub-orders and execute them gradually over several hours. The purpose of doing this is to give the market enough time to realize that she doesn't have insider information and give her better prices. However, even if she sends several larger sub-orders, each sub-order still has a significant impact on the price and is vulnerable to "sandwich attacks" from hostile traders.

TWAMM solves this problem by trading on behalf of Alice. It decomposes her order into an infinite number of tiny virtual orders to ensure smooth execution over time. At the same time, TWAMM leverages the special mathematical relationship of embedded AMM protocols to share the gas cost among these virtual orders. And since TWAMM processes transactions between blocks, it is also less susceptible to "sandwich attacks".

In general, TWAMM provides Alice with a more efficient way to conduct large-scale transactions, avoiding high transaction fees and potential market manipulation.

2.1.1 Principle

TWAMM has a built-in AMM, which is not different from other AMMs. Users can directly conduct spot transactions through this AMM, and also add liquidity to it. However, TWAMM also has two TWAP order pools to execute TWAP orders in two directions. When a user submits an order, specifying the token input quantity and duration of the transaction, TWAMM will place the order in the corresponding pool of the same direction, and automatically execute the transaction at the specified trading speed. Once the user's order is fully executed, the user can withdraw the tokens obtained from the transaction. Of course, the user can also cancel or modify the order before it is fully executed.

In Ethereum, smart contracts can only be triggered and executed by EOA addresses actively sending transactions, and cannot be executed automatically. Therefore, TWAMM needs an EOA account to regularly send transactions to settle the tokens awaiting trading in its order pool, requiring a keeper account to execute these transactions.

Alternatively, TWAMM can automatically settle the order pool each time a user interacts with it, eliminating the need for a keeper, which is a common approach for DeFi protocols dealing with streaming data.

2.1.2 Why is this trading model difficult to be sandwich attacked?

It is difficult to execute this attack because the timestamp of a block does not change. The attacker must manipulate the pool's price in the last transaction of a block, which would affect the settlement of TWAMM in the next block. This requires the sandwich attack to occur in multiple blocks, which undoubtedly exposes the attacker to great risk, as other arbitrageurs may intervene in between, causing the attacker to suffer losses.

At the same time, because of the existence of arbitrageurs, such price manipulation is destined not to last. Due to the nature of TWAP order, it will not trade too many tokens in a short period of time, so in most cases, the loss is also 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 to be traded, the quantity, and the duration

(3) This Hook registers beforeSwap and beforeModifyPosition, which are triggered every time the user trades or adjusts the position

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

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

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

2.1.4 Example Explanation

TWAMM registers three stages for logical call of hooks. It initializes TWAMM before pool initialization and triggers this hook every time the user trades or adjusts the position.

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

In the user adds the order they need to execute to the contract, when the pool performs swap and modifyPosition operations, the orders will be automatically executed.

Every time a user calls the v4 swap function to trade or the modifyPosition function to change the position, the execution function in TWAMM will be triggered, and the function will call the internal function _executeTWAMMOrders to continue the execution of the unfinished orders.

_executeTWAMMOrders function

The above is the execution process for updating orders. After the execution is completed, the current twamm order execution time will be updated.

2.2 Limit Order

Unlike market orders, which are executed immediately at the last market price, limit orders are executed as soon as the predetermined price is reached. Most DEXs based on automated market makers (AMMs) default to market order systems, which are simple and easy to understand for beginners. Market orders are either executed or fail due to parameters such as maximum price impact. In limit orders, the order is only executed when the asset price reaches the limit price, otherwise the order will remain open.

For example, let's assume that the current trading price of ETH in the ETH/DAI pool is 1 ETH = 1500 DAI. Users can place a stop profit order with the main content "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 the blockchain.

In previous versions of Uniswap, limit orders were actually not possible. Most AMMs only allowed market buy and sell orders. However, in V4, the powerful features and scalability of hooks allow for the implementation of limit orders.

2.2.1 Principle

The design principle of limit orders is simpler compared to TWAMM, and currently, adding liquidity-related limit orders has been implemented, making it easier to implement limit orders for trading.

Since V4 has tickLower and tickUpper, which change based on the trading situation of the pool, if users do not want to add liquidity at the current price when adding liquidity, they can use the limit order hook to execute this requirement. In the hook, they can set the corresponding price, and after each swap, the hook will check the current price of the pool. If the set price is reached, the corresponding liquidity will be added to obtain profits.

2.2.2 Workflow of Limit Order in V4

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

2. Users submit their own prices, lower, and trading directions through this hook to add limit orders to the contract.

3. This hook is registered as afterSwap and only triggers when the price changes after each swap.

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

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

2.2.3 Detailed Example

The contract triggers a hook in two stages. The hook initializes after Pool initialization and triggers hook logic after each exchange.

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

This hook is triggered after each 'swap' and performs the liquidity addition operation based on the current price range and any limit orders that exist within that range.

Users can call the 'kill' function before the limit order is completed to cancel the limit order and receive the profit from adding liquidity.

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

In general, 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 this price, the hook automatically adds liquidity to the pool for the user. Users can also cancel and withdraw liquidity at any time.

In addition to TWAMM and Limit Order, hooks can also be used for LP reinvestment, dynamic fee changes, and other functionalities. Due to space limitations, we will discuss these in more detail in subsequent analyses:

LP reinvestment: LP users can use hooks to add, modify, and remove liquidity. Hooks can be registered for afterSwap and afterModifyPosition calls. Since users use hooks uniformly to add liquidity, the address for adding liquidity in the poolManager is only the hook. The hook can check the current time at each trigger and after a certain time interval, choose to extract liquidity rewards and add the obtained LP tokens back to the pool, thereby automating the optimization of user earnings.

Dynamic fee changes: Hooks can register beforeSwap and other interfaces to modify dynamic fees before the exchange. Dynamic fees can change not only linearly based on time but also quantitatively based on the tick jump quantity generated by each swap to quantify volatility and dynamically change the fees. This helps hedge against impermanent loss risks for LP providers, reducing the impact caused by impermanent loss resulting from trading.

3. Hooks Security Best Practices

Reduce the Use of require and revert

In the function logic related to hooks calls on the pool, try to minimize the use of fallback statements. Due to the common relationship between the pool contract and the hooks contract, when a transaction rollback occurs in the hooks, the transactions in the pool will also be rolled back. If there are rollback statements in the hooks that are unrelated to the normal transactions in the pool, it may prevent users from using the functionalities of the pool properly.

Avoid the Use of Self-Destruct Functions

Avoid using the selfdestruct function in hooks. If the self-destruct function is called in the hooks, it will not only cause issues with the logic in the hooks, but also disable the functionalities in the pool, resulting in the loss of assets and the inability to use the functionalities in the entire pool.

Enforce Strict Access Control

Strictly control the permissions in the hook contract to avoid roles with excessive privileges. Implement multi-signature management for privileged roles to prevent single-point attacks. Avoid situations where privileged roles can modify contract state variables arbitrarily, as it may cause logical errors and result in transaction rollbacks, impacting the normal usage of the pool. Follow the principle of least privilege, and use the AccessControl contract from OpenZeppelin to achieve finer-grained access control, as this practice restricts each system component to follow the principle of least privilege.

Implement Protection Against Reentrancy

hooks as external extension code for Pool should also be aware of possible reentrant attacks in contracts, such as the possibility of reentry when transferring native tokens in limit orders, resulting in loss of contract assets. Check and attempt to update all states before calling external contracts or using the "check-effect-interact" pattern. This way, even if there is a reentrant call, it will not have any impact as all states have been fully updated.

Contract Upgrade Control

Some developers may use proxy contracts for future logic changes and upgrades to hooks, but this approach also requires attention to potential issues with contract upgrades. Firstly, even with the proxy pattern, the proxy contract needs to declare corresponding stages, such as beforeSwap, otherwise the pool cannot verify the correct return values. Secondly, make sure to check the existence of the target contract before making a delegate call. Solidity does not perform this check for us, and ignoring this check may lead to unexpected behavior and security issues. Carefully consider the order of variable declarations, as it may result in variables packing into the same slot, affecting gas cost, memory layout, delegate call results, and other issues.

About Us

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

Website | Twitter


Uniswap
DeFi
Safety
Welcome to Join Odaily Official Community