- January 26, 2026
OpenZeppelin Security
OpenZeppelin Security
Security Audits
Summary
Type: DeFi
Timeline: From 2025-11-13 → To 2025-11-14
Languages: Solidity
Findings
Total issues: 10 (6 resolved)
Critical: 0 (0 resolved) · High: 0 (0 resolved) · Medium: 0 (0 resolved) · Low: 3 (1 resolved)
Notes & Additional Information
7 notes raised (5 resolved)
Scope
OpenZeppelin audited the Uniswap/protocol-fees repository at commit b4a996c.
In scope were the following files:
src
├── UNIVesting.sol
└── interfaces
└── IUNIVesting.sol
System Overview
The UNIVesting contract releases UNI tokens quarterly to a designated recipient on a calendar-based schedule. Vesting begins January 1, 2026, and continues every three calendar months.
The owner must approve UNI tokens to the UNIVesting contract for it to transfer the vested tokens. When a quarter elapses, anybody can call withdraw to trigger a transfer of tokens from the owner to the recipient. The quarterly vesting amount is initially set to 5,000,000 UNI but can be adjusted by the owner when no quarters are claimable. The contract supports partial withdrawals: if the owner's allowance is insufficient to cover all vested quarters, the contract calculates how many complete quarters can be withdrawn with the available allowance and transfers only that amount.
When the contract is initialized, lastUnlockTimestamp is set to three months before January 1, 2026 to ensure that the first withdrawal will be possible on January 1, 2026. After each withdrawal, lastUnlockTimestamp is updated to the end of the last claimable quarter instead of the actual timestamp of the withdrawal.
Security Model and Trust Assumptions
Throughout the in-scope codebase, the following trust assumptions were identified:
- Owner token custody: The owner retains custody of unvested UNI tokens and must maintain sufficient ERC-20 allowance for the contract to execute withdrawals. If the owner revokes allowance, the recipient cannot withdraw vested tokens until allowance is restored.
- Hardcoded values: The
UNIVestingcontract is hardcoded to begin on January 1, 2026 with an initial quarterly release rate of 5,000,000 UNI. If this contract is used for purposes other than UNI vesting, these values may need to be changed.
Privileged Roles
Throughout the in-scope codebase, the following privileged roles and actions were identified:
- The owner can update the quarterly vesting amount and change the recipient address.
- The recipient can change the recipient address.
Low Severity
Possible Duplicate Event Emission
When a setter function does not check if the value being set is different from the existing one, it becomes possible to set the same value repeatedly, creating a potential for event spamming. Repeated emission of identical events can also confuse off-chain clients.
- In the
updateRecipientfunction, there is no check to ensure that the recipient being set is different from the current one. As a result, an event indicating a change in the recipient is emitted even when the new recipient is identical to the existing one. - Within
UNIVesting.sol, theupdateVestingAmountsetsquarterlyVestingAmountand emits an event without checking if the value has changed.
Consider adding a check that reverts the transaction if the value being set is identical to the existing one.
Update: Resolved in PR 92 at commit 143445.
Floating Pragma
Pragma directives should be fixed to clearly identify the Solidity version with which the contracts will be compiled. UNIVesting.sol has the solidity ^0.8.29 floating pragma directive.
Consider using fixed pragma directives.
Update: Acknowledged, not resolved. The team stated:
We are ok with floating pragmas in this circumstance.
Missing Zero-Address Checks
When operations with address parameters are performed, it is crucial to ensure that the address is not set to zero.
Within UNIVesting.sol, multiple instances of missing zero-address checks were identified:
- The
_unioperation - The
_recipientoperation - The
_recipientoperation
Consider always performing a zero-address check before assigning a variable or doing an operation with address parameters.
Update: Acknowledged, not resolved. The team stated:
We think this is unnecessary protection - just as likely to set it as an incorrect nonzero address as the zero address incorrectly.
Notes & Additional Information
Inconsistent Use of Returns
The quarters function has an inconsistent usage of returns. It uses named returns to return quartersPassed, whereas in line 117, it uses the return keyword.
Consider being consistent with the use of returns.
Update: Resolved. Fixed in PR 92 at commit fb86fe6.
Functions Updating State Without Event Emissions
Within UNIVesting.sol, the withdraw function does not emit an event when UNI tokens are withdrawn.
Consider emitting events whenever there are state changes.
Update: Resolved. Fixed in PR 92 at commit fb42f64.
Lack of Indexed Event Parameters
Within IUNIVesting.sol, multiple instances of events not having any indexed parameters were identified:
To improve the ability of off-chain services to search and filter for specific events, consider indexing event parameters.
Update: Acknowledged, not resolved. The team stated:
These are super rare events s/t indexing should not be a problem. Just looking by topic and contract address should return enough to sort quickly.
Function Visibility Overly Permissive
Within UNIVesting.sol, multiple instances of functions having overly permissive visibility were identified:
- The
updateVestingAmountfunction inUNIVesting.solwithpublicvisibility could be limited toexternal. - The
updateRecipientfunction inUNIVesting.solwithpublicvisibility could be limited toexternal.
To better convey the intended use of functions and to potentially realize some additional gas savings, consider changing a function's visibility to be only as permissive as required.
Update: Resolved. Fixed in PR 92 at commit cc290d.
Multiple Optimizable State Reads
In UNIVesting.sol, multiple instances of optimizable storage reads were identified:
- The multiple storage reads (1, 2, 3) in
quarterlyVestingAmount - The multiple storage reads (1, 2) in
lastUnlockTimestamp
Consider reducing SLOAD operations that consume unnecessary amounts of gas by caching the values in a memory variable.
Update: Resolved. Fixed in PR 92 at commit 4c403d.
Over-Constrained Interface Type
The IUNIVesting interface returns a concrete Solmate ERC-20 type for UNI(), forcing all implementers to use or wrap Solmate's ERC-20 instead of any standard ERC-20 interface. This tight coupling limits compatibility, increases integration friction, and adds unnecessary dependency requirements without functional benefit.
Consider returning a minimal ERC-20 interface or address instead of Solmate ERC-20 to decouple implementations and improve flexibility.
Update: Acknowledged, not resolved. The team stated:
Don't really expect this to get integrated too terribly much, and we use the solmate SafeTransferLib which is more ergonomic when dealing with just solmate ERC20 types
Misleading Documentation
The lastUnlockTimestamp function in IUNIVesting.sol has conflicting NatSpec comments and a mismatched return type. The documentation describes both unlock time and withdrawal time, while the function actually returns a uint48 schedule boundary of the last settled quarter. This inconsistency can mislead integrators into using the value incorrectly in on-chain or off-chain logic.
Consider clarifying the NatSpec comments to accurately describe the variable's purpose and type to reflect its true semantics.
Update: Resolved. Fixed in PR 92 at commit 76086b.
Conclusion
The UNIVesting contract enables quarterly vesting of UNI tokens to a recipient starting January 1, 2026, with withdrawals that can be triggered by anyone, adjustable release amounts by the owner, and transfers limited by the owner's ERC-20 allowance and balance. The audit identified several minor issues, leading to recommendations for improving code consistency and readability.
The Uniswap Labs team is appreciated for being highly cooperative and providing clear explanations throughout the audit process.
Appendix
Issue Classification
OpenZeppelin classifies smart contract vulnerabilities on a 5-level scale:
- Critical
- High
- Medium
- Low
- Note/Information
Critical Severity
This classification is applied when the issue’s impact is catastrophic, threatening extensive damage to the client's reputation and/or causing severe financial loss to the client or users. The likelihood of exploitation can be high, warranting a swift response. Critical issues typically involve significant risks such as the permanent loss or locking of a large volume of users' sensitive assets or the failure of core system functionalities without viable mitigations. These issues demand immediate attention due to their potential to compromise system integrity or user trust significantly.
High Severity
These issues are characterized by the potential to substantially impact the client’s reputation and/or result in considerable financial losses. The likelihood of exploitation is significant, warranting a swift response. Such issues might include temporary loss or locking of a significant number of users' sensitive assets or disruptions to critical system functionalities, albeit with potential, yet limited, mitigations available. The emphasis is on the significant but not always catastrophic effects on system operation or asset security, necessitating prompt and effective remediation.
Medium Severity
Issues classified as being of medium severity can lead to a noticeable negative impact on the client's reputation and/or moderate financial losses. Such issues, if left unattended, have a moderate likelihood of being exploited or may cause unwanted side effects in the system. These issues are typically confined to a smaller subset of users' sensitive assets or might involve deviations from the specified system design that, while not directly financial in nature, compromise system integrity or user experience. The focus here is on issues that pose a real but contained risk, warranting timely attention to prevent escalation.
Low Severity
Low-severity issues are those that have a low impact on the client's operations and/or reputation. These issues may represent minor risks or inefficiencies to the client's specific business model. They are identified as areas for improvement that, while not urgent, could enhance the security and quality of the codebase if addressed.
Notes & Additional Information Severity
This category is reserved for issues that, despite having a minimal impact, are still important to resolve. Addressing these issues contributes to the overall security posture and code quality improvement but does not require immediate action. It reflects a commitment to maintaining high standards and continuous improvement, even in areas that do not pose immediate risks.
Ready to secure your code?