Uniswap V4 WETH and WstETH Hooks Audit

Written by OpenZeppelin Security | May 6, 2025 4:00:00 AM

Table of Contents

Summary

Type
DeFi
Timeline
From 2025-02-24
To 2025-02-26
Languages
Solidity
Total Issues
4 (4 resolved)
Critical Severity Issues
0 (0 resolved)
High Severity Issues
1 (1 resolved)
Medium Severity Issues
0 (0 resolved)
Low Severity Issues
2 (2 resolved)
Notes & Additional Information
1 (1 resolved)

Scope

We audited the Uniswap/v4-periphery repository at commit 9628c36.

In scope were the following files:

 src
├── base
│   └── hooks
│       └── BaseTokenWrapperHook.sol
├── hooks
│   ├── WETHHook.sol
│   └── WstETHHook.sol
└── utils
    └── BaseHook.sol

System Overview

This system implements a set of token wrapper hooks for Uniswap V4, enabling seamless wrapping and unwrapping of tokens within decentralized exchange pools. The architecture consists of a base hook contract that enforces core wrapping logic, with specialized implementations for specific tokens such as WETH and wstETH. It ensures that liquidity operations are restricted while allowing automated token conversions during swaps. By facilitating automated wrapping and unwrapping, these hooks enhance the usability and composability of Uniswap V4. The modular design provides flexibility for supporting additional token wrapping mechanisms in the future while maintaining robust security measures and trust assumptions.

BaseTokenWrapperHook

The BaseTokenWrapperHook contract serves as the foundational layer for the token-wrapping logic in Uniswap V4 pools and performs the following key functions:

  • Enforces liquidity management restrictions by preventing liquidity from being added, as liquidity is managed through the underlying token wrapper.
  • Validates pool initialization by ensuring that pools contain exactly one wrapper token and its underlying token, with a zero-fee structure.
  • Defines hook permissions, implementing pre-swap handling for automated wrapping and unwrapping while blocking liquidity-related operations.
  • Determines the wrapping direction and executes appropriate deposit or withdrawal operations.
  • Defines abstract methods for depositing, withdrawing, and computing input requirements for wrapping and unwrapping tokens.

WETHHook

The WETHHook contract extends BaseTokenWrapperHook and performs the following key functions:

  • Provides a seamless mechanism for wrapping and unwrapping ETH into WETH.
  • Uses the WETH contract for deposit and withdrawal operations and maintains a 1:1 exchange rate for ETH to WETH conversions.
  • Implements a fallback function to accept ETH deposits.

WstETHHook

The WstETHHook contract extends BaseTokenWrapperHook to support dynamic exchange rate conversions between stETH and wstETH, and performs the following key functions:

  • Uses Lido’s wstETH contract for wrapping and unwrapping operations.
  • Incorporates the dynamic exchange rate between stETH and wstETH to maintain accurate conversions.
  • Implements specific methods for calculating the required inputs based on the current exchange rate mechanics provided by wstETH contract.
  • Ensures ERC-20 approval for interactions with the wstETH contract.

Security Model and Trust Assumption

The security of the system under review depends on the correctness of the core infrastructure of Uniswap V4 and the Pool Manager’s execution of swaps without vulnerabilities. It assumes that external contracts, such as WETH and wstETH, function as intended and do not introduce security flaws. Furthermore, the system relies on the accuracy of token conversions and the expected exchange rates for stETH and wstETH. Any deviation from these assumptions could impact the system's intended functionality.

 

High Severity

Exact Output Swaps Could Revert Due to Rounding

When performing exact output swaps, the _getWrapInputRequired function calculates the amount of underlying tokens required to be wrapped in order to achieve the exact desired output amount. Since conversions for wrapping round down (in favor of the protocol), the inverse function, which determines how much underlying must be wrapped to achieve the exact output, must round up.

In the wstEthHook contract, the getStETHByWstETH function rounds in the wrong direction. The input amount is calculated by getStETHByWstETH, which in turn calls getPooledEthByShares, a function that rounds down. As a result, after wrapping, if one more asset is required to achieve the exact output, the pool manager attempts to swap through the pool. If the pool lacks sufficient liquidity, the swap attempt will revert. This issue also arises when unwrapping wstETH to achieve an exact output, due to the rounding in the _getUnwrapInputRequired function.

In order to account for the rounding direction, when wrapping, consider dividing the desired output by the conversion rate used for wrapping and adding 1 if there is a remainder. In addition, consider adding comments above the _getWrapInputRequired and _getUnwrapInputRequired functions to clarify the correct rounding direction when wrapping and unwrapping assets.

Update: Resolved in pull request #460. The Uniswap team stated:

After much evaluation of the rounding logic in stETH and fork tests, we came to the conclusion that it is not possible to safely and efficiently precalculate the input amount required precisely and robustly, so opted to disable exact output for this hook.

Low Severity

Incomplete Docstring

Within BaseHook.sol, the getHookPermissions function is missing documentation for the return value using @return.

Consider adding documentation for the return value following the Ethereum Natural Specification Format (NatSpec).

Update: Resolved in pull request #461.

Floating Pragma

Pragma directives should be fixed to clearly identify the Solidity version with which the contracts will be compiled. Throughout the codebase, multiple instances of floating pragma directives were identified:

Consider using fixed pragma directives.

Update: Resolved in pull request #461.

Notes & Additional Information

Unused Named Return Variables

Named return variables are a way to declare variables that are meant to be used within a function's body for the purpose of being returned as that function's output. They are an alternative to explicit in-line return statements.

Within BaseTokenWrapperHook.sol, multiple instances of unused named return variables were identified:

Consider either using or removing any unused named return variables.

Update: Resolved in pull request #461.

 
 

Conclusion

The token wrapper hooks enhance Uniswap V4 by enabling the automatic wrapping and unwrapping of tokens, thereby improving usability and composability. During the audit, one high-severity issue was identified, and various recommendations aimed at enhancing code consistency and readability were made.

Throughout the audit process, the Uniswap team was highly cooperative and provided clear explanations.