Platform: Code4rena
Start Date: 23/02/2024
Pot Size: $92,000 USDC
Total HM: 0
Participants: 47
Period: 10 days
Judge: 0xTheC0der
Id: 336
League: ETH
Rank: 40/47
Findings: 1
Award: $22.02
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: roguereggiant
Also found by: 0xepley, Aamir, Al-Qa-qa, LinKenji, MSK, McToady, Myd, SAQ, Sathish9098, ZanyBonzy, aariiif, cudo, emerald7017, fouzantanveer, hassanshakeel13, hunter_w3b, ihtishamsudo, kaveyjoe, peanuts
22.023 USDC - $22.02
The UniStaker infrastructure aims to empower Uniswap governance through a UNI token staking system. It consists of two core components:
Key Components
V3FactoryOwner
: Owns Uniswap V3 factories. Enables protocol fees and routes them to stakers.UniStaker
: Distributes streaming rewards to UNI stakers over locked periods.Objectives
The system intends to achieve:
graph TD subgraph Uniswap Ecosystem V3Factories V3Pools end V3Factories --> |"Owns and<br>Governs"|V3FactoryOwner V3Pools --> |"Protocol Fees"|V3FactoryOwner V3FactoryOwner --> |"Forwards Fees"|UniStaker Users --> |"Stake for Rewards"|UniStaker UniStaker --> |"Distributes Token Rewards"|Stakers Stakers --> |"Rewards Incentivize"|GovernanceVotes
Several key attack vectors exist:
Countermeasures include audits, access controls, monitoring, and incentives modeling.
V3 Factory Ownership
The V3FactoryOwner
holds privileged ownership of all Uniswap v3 factories. This lets it configure fees and pools. To sustain incentives, it continually auctions fee revenue from all pools to any actor willing to pay the protocol fixed payoutAmount
of designated payoutToken
.
function claimFees( address pool, address recipient, uint128 amount0Requested, uint128 amount1Requested ) external { payoutToken.transferFrom(msg.sender, rewardReceiver, payoutAmount)); pool.collectProtocolFees(recipient, amount0Requested, amount1Requested); }
UNI Staking Rewards
The UniStaker
contract lets UNI holders stake tokens to earn additional rewards over 30 days reward periods. Rewards accrue dynamically based on share of total stake. Stakers keep voting rights and can withdraw any time.
function rewardPerToken() view returns (uint256) { return rewardsPerToken + totalRewards * (block.timestamp - lastUpdate) / totalStakedTokens ; } function earned(address account) view returns (uint256) { return stakedBalances[account] * (rewardPerToken() - userRewardPerTokenPaid[account]) / POINT_SCALE; }
This full analysis covers key concepts, interactions, objectives, risks, and mitigations around this Uniswap infrastructure. Please let me know if any part requires more detail or clarification!
The UniStaker system enables Uniswap governance token (UNI) holders to stake their tokens and earn rewards from protocol fees collected in WETH. The architecture centers on two core contracts:
Key Contracts
Contract | Purpose |
---|---|
V3FactoryOwner | Owns Uniswap V3 Factory. Enables fee collection from V3 pools by external parties. |
UniStaker | Manages staking deposits and reward distribution to stakers. |
Architecture
digraph architecture { node [fontname="Courier", shape=record]; subgraph cluster_1 { label = "External World"; weth [label="WETH Token"]; uni [label="UNI Token"]; mev [label="MEV Bots"]; } subgraph cluster_2 { label = "Core Protocol"; factoryOwner [label="V3FactoryOwner"]; uniStaker [label="UniStaker"]; } mev -> factoryOwner weth -> mev [label="payout\namount"]; factoryOwner -> uniStaker uni -> uniStaker; uniStaker -> weth [label="rewards"]; }
Economic incentives drive external MEV bots to continually pay the payoutAmount
of WETH to the V3FactoryOwner
to claim protocol fees from Uniswap V3 pools. These WETH rewards are forwarded to the UniStaker
for distribution to UNI stakers.
Analysis of Key Issues
Issue | Description | Mitigations |
---|---|---|
Reward Notifier Griefing | Dishonest or buggy reward notifier contracts could exploit stakers | Strict admin controls over enabled notifiers |
Price Ratio Divergence | Payout token/fee token price differences break auction incentives | Configurable price drift circuit breakers, adjustable payout amount |
Changes to Parameters | Modifying staking parameters can have unanticipated effects | Formal governance processes for adjustments |
Pool Interaction Bugs | Complex pool states could introduce issues | Comprehensive integration testing, fuzzing |
Scenario Analysis
Scenario demonstrating the griefing that dishonest reward notifiers could inflict:
Alice registers as a UNI staker, delegating her voting power and depositing her UNI in the UniStaker contract. Eve deploys TokenRewarder, a malicious contract that integrates with UniStaker by notifying fake 1 wei rewards every 5 seconds. The UniStaker admin whitelist TokenRewarder as a legitimate reward notification source. Eve's frequent tiny notifications cause the reward period to be persistently extended, diluting rewards for Alice and other honest stakers.
The trust assumptions placed in reward notifiers and the potential for abuse.
setAdmin()
- Allows admin to set a new admin addresssetPayoutAmount()
- Allows admin to set the payout amount for claimFees()
enableFeeAmount()
- Allows admin to enable fee amounts on the Uniswap V3 factorysetFeeProtocol()
- Allows admin to set protocol fees on V3 poolsUniStaker Contract
setAdmin()
- Allows admin to set a new admin addresssetRewardNotifier()
- Allows admin to enable/disable reward notifier addressesnotifyRewardAmount()
- Allows enabled reward notifiers to notify about new rewardsVulnerability Assessment
Centralization Risks
Authorization Flaws
Admin functions lack sufficient authorization controls. For example, setAdmin()
does not check/validate the proposed new admin address.
notifyRewardAmount()
in UniStaker relies on a simple whitelisting of "reward notifiers". This could be gamed by a malicious admin approving bad actors.
function notifyRewardAmount(uint256 _amount) external { if (!isRewardNotifier[msg.sender]) revert UniStaker__Unauthorized("not notifier", msg.sender); // We checkpoint the accumulator without updating the timestamp at which it was updated, because // that second operation will be done after updating the reward rate. rewardPerTokenAccumulatedCheckpoint = rewardPerTokenAccumulated(); if (block.timestamp >= rewardEndTime) { scaledRewardRate = (_amount * SCALE_FACTOR) / REWARD_DURATION; } else { uint256 _remainingReward = scaledRewardRate * (rewardEndTime - block.timestamp); scaledRewardRate = (_remainingReward + _amount * SCALE_FACTOR) / REWARD_DURATION; } rewardEndTime = block.timestamp + REWARD_DURATION; lastCheckpointTime = block.timestamp; if ((scaledRewardRate / SCALE_FACTOR) == 0) revert UniStaker__InvalidRewardRate(); // This check cannot _guarantee_ sufficient rewards have been transferred to the contract, // because it cannot isolate the unclaimed rewards owed to stakers left in the balance. While // this check is useful for preventing degenerate cases, it is not sufficient. Therefore, it is // critical that only safe reward notifier contracts are approved to call this method by the // admin. if ( (scaledRewardRate * REWARD_DURATION) > (REWARD_TOKEN.balanceOf(address(this)) * SCALE_FACTOR) ) revert UniStaker__InsufficientRewardBalance(); emit RewardNotified(_amount, msg.sender); }
Upgradeability Risks
Lack of Transparency
setAdmin()
and setRewardNotifier()
do not emit events about the admin change.Mitigation Recommendations
Use a Timelock for admin address and critical parameter changes. This prevents rapid unilateral changes.
Implement a multi-signature scheme for admin actions, requiring approval from multiple independent entities.
Add more rigorous authorization controls on admin functions, such as:
Emit events from all state-changing admin functions to create an immutable record of changes.
Consider breaking up the UniStaker admin role into multiple specialized roles with explicit permissions. This reduces risks from a single all-powerful admin.
Formal verification of core authorization and state-changing logic could prevent bypassing of intended admin constraints.
V3FactoryOwner
interacts with the Uniswap V3 factory and pool contracts. While these are complex, they are battle-tested core Uniswap contracts that can reasonably be considered secure dependencies.
V3FactoryOwner
sends payouts to the UniStaker contract. The simplicity and focused purpose of UniStaker reduces the risk surface for issues cascading from it.
Unforeseen Interactions
The open ability to buy pool fees could be combined with flash loans to manipulate pricing or drain funds in unexpected ways. Adding checkpoints on the pricing of a fee purchase could prevent some flash loan exploits.
Economic and Market Considerations
Liquidity Risks
Incentive Misalignment
Governance Risks
Centralization
Lack of Adaptability
Mitigation Recommendations
Make admin roles multi-signature across multiple ecosystem partners to avoid centralized control.
Implement a DAO-governed process for admin role and parameter management over time to enhance adaptability.
Conduct stress tests around flash loan interactions with the external fee purchase mechanism to identify and address potential attack vectors.
Formal verification of the core pool fee purchase logic could inspire confidence by ruling out broad categories of unanticipated exploits.
The UniStaker contract allows UNI token holders to stake their tokens and earn staking rewards over time. It is designed to incentivize long-term locking of UNI tokens to improve governance participation.
Architecture
+----------------------+ +-------------------------+ | | | | | User 1 +-------->+ UniStaker | | | | | +----------------------+ ++------------------+----+ | +-------------+ | | +-----------------------+---------------+ | | | | User 2 +------------->+ | | | +-----------------------+ | | +-------------+ | | +----------------------+ | | +--+ | User N | | | +----------------------+
Key Contracts
+----------+ +---------------+ | | | | | UNI +----------->+ UniStaker | | | | | +----------+ +-------+-------+ | | +--------------------+--------------------+ | | | +----------+ +-------v------+ +------v-------+ | | | | | | | Factory +----------->+ Surrogate 1 | | Surrogate N | | | | | | | +----------+ +--------------+ +--------------+
Key Functions
+---------------------+ | | | UniStaker | | | +-----------+---------+ | | +-----------------------------------+ | | | | | Administrative Functions | | | | | +-----------------------------------+ | | - setAdmin() | - setRewardNotifier() | | +------v------+ | | | +-----------------------------------+ | | | | | Staking Functions | | | | | +-----------------------------------+ | | - stake() | - withdraw() | - claimReward() | | . . .
Key Risks and Mitigations
+------------------+------------------------+--------------------------+ | Issue | Description | Mitigation | +------------------+------------------------+--------------------------+ | Centralized | UniStaker admin | Decentralize admin | | Admin | has excess power | using DAO or | | | over system | multi-signature wallet | +------------------+------------------------+--------------------------+ | Economic | Potential for | Implement epochs | | Attacks | nourishing attacks | for gradual wind | | | if parameters are | down of rewards/ | | | changed abruptly | parameters | +------------------+------------------------+--------------------------+ | Contract Risk | Risks from reliance | Formal verification | | Cascades | on external contracts | of integration points | | | like Factory and UNI | | +------------------+------------------------+--------------------------+
30 hours
#0 - c4-judge
2024-03-14T18:18:33Z
MarioPoneder marked the issue as grade-b