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: 44/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 contract allows Uniswap governance token holders to stake their tokens and earn additional rewards. It distributes staking rewards over time to designated beneficiary addresses. Users can stake to a specific deposit, delegate voting power, and assign a beneficiary address in a highly flexible manner.
Architecture
ββββββββββββββββββββββββ ββββββββββββββββββββββββ β β β β β User 1 ββββββΆ Reward β β β β Token β β ββββββββββββββββββ β β β β β β β β β β β Deposit βββββββββββββββββββββββββββββββββ β β β β β² ERC20 Transfer β ββββββββββββββββββ β β Notify Rewards β β²Delegate β β β β β β β βββββββ β ββββββββββββββββββββββββ β β βΌ ββββββββββββββ βββββ>βDelegatee 1 β β ββββββββββββββ Deposit surpluses β aggregated by delegatee β ββββββββββββββ ββββββ>βDelegatee 2 β ββββββββββββββ
Key Contracts
UniStaker
- Core staking and rewards distribution logicDelegationSurrogate
- Holds tokens on behalf of delegateesV3FactoryOwner
- Manages protocol fees collectionStrengths
Weaknesses
V3FactoryOwner
Attack Scenarios
Attack | Description | Mitigation |
---|---|---|
Rogue Admin | Admin could steal staked tokens or distribute unfair rewards | Decentralize admin, adopt timelock |
Reward Rate Manipulation | Attackers can notify tiny rewards to reduce rate, discouraging stakers | Implement minimum reward thresholds |
Recommendations
The UniStaker infrastructure enables flexible and permissionless Uniswap governance participation but suffers from some centralized admin risks that could be mitigated through decentralization, trust minimization, and security best practices.
Scope
Contracts (3)
Interfaces (4)
src/interfaces/IERC20Delegates.sol
src/interfaces/INotifiableRewardReceiver.sol
src/interfaces/IUniswapV3FactoryOwnerActions.sol
src/interfaces/IUniswapV3PoolOwnerActions.sol
The key purpose is to empower Uniswap governance through rewards for stakers. Core functionality centers around UniStaker for distribution, V3FactoryOwner for managing fees funding rewards, and DelegationSurrogate for retaining voting power of staked tokens.
admin
- Can set new admin, enable/disable reward notifiersnotifyRewardAmount
- Called by reward notifiers to notify rewards
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); }
setRewardNotifier
- Admin can enable/disable reward notifier addressesfunction setRewardNotifier(address _rewardNotifier, bool _isEnabled) external { _revertIfNotAdmin(); isRewardNotifier[_rewardNotifier] = _isEnabled; emit RewardNotifierSet(_rewardNotifier, _isEnabled); }
admin
- Can set new admin, enable fee amounts, set protocol feessetAdmin
- Sets new admin
function setAdmin(address _newAdmin) external { _revertIfNotAdmin(); if (_newAdmin == address(0)) revert V3FactoryOwner__InvalidAddress(); emit AdminSet(admin, _newAdmin); admin = _newAdmin; }
enableFeeAmount
- Enables fee amount on Uniswap V3 factoryfunction enableFeeAmount(uint24 _fee, int24 _tickSpacing) external { _revertIfNotAdmin(); FACTORY.enableFeeAmount(_fee, _tickSpacing); }
setFeeProtocol
- Sets protocol fee on V3 poolsfunction setFeeProtocol( IUniswapV3PoolOwnerActions _pool, uint8 _feeProtocol0, uint8 _feeProtocol1 ) external { _revertIfNotAdmin(); _pool.setFeeProtocol(_feeProtocol0, _feeProtocol1); }
setPayoutAmount
- Sets payout amount for claimFees
function setPayoutAmount(uint256 _newPayoutAmount) external { _revertIfNotAdmin(); if (_newPayoutAmount == 0) revert V3FactoryOwner__InvalidPayoutAmount(); emit PayoutAmountSet(payoutAmount, _newPayoutAmount); payoutAmount = _newPayoutAmount; }
Vulnerability Assessment
Centralization Risks
UniStaker
highly centralized around admin
role - full control over rewards
V3FactoryOwner
also relies heavily on centralized admin
No checks on admin actions besides basic address validation
Authorization Flaws
No role-based access control in either contract
notifyRewardAmount
in UniStaker
callable by any enabled notifier
No re-entrancy guards on external calls like notifyRewardAmount
Upgradeability Risks
Lack of Transparency
Some key admin functions like setAdmin
don't emit events
Vital actions like reward notifications not logged
Mitigation Recommendations
Use a DAO/multisig scheme instead of single admin address
Implement a timelock for critical admin functions
Add re-entrancy guards and input validation where possible
Emit events for all state changes, especially admin actions
Add RBAC to restrict capabilities rather than central admin
Frequent audits to catch issues early
Consider immutable storage for critical parameters
Additional Considerations
Simplify UniStaker
rewards distribution mechanics if possible
Audit any external dependencies like Uniswap contracts
Understand the intent and context of the contracts - centralized control may be a design choice
As contracts grow in usage and value, prioritize decentralization and trust minimization
+---------------------------------------------------+ | | | UniStaker | | | +-+------------------+------------------+----------+ ^ ^ ^ | | | Reward with Notify reward Manage governance tokens amount reward notifiers +------------------------+ | | +--------------->| V3FactoryOwner |<---------------+ | Pay fee | | Set protocol | +------+ | | | fees on pools | +--------+ | User | ---->+ Stake tokens +------------------------+ Factory admin |---->| Uniswap | +------+ | ^ | | Factory | | | | | +---+----+ | Claim +------------------------+ | reward | + Pool fees | V +----------+ | | | Uniswap | | Pools | | | +----------+
User Flow
Admin Flow
Protocol Roles
External Dependencies
Oracles
Other Contracts
UniStaker
interacts with REWARD_TOKEN
and STAKE_TOKEN
which should be audited
V3FactoryOwner
interacts heavily with Uniswap V3 contracts like the factory and pools
Issues with any of these contracts could cascade to the staking system
Composability Risks
Unforeseen Interactions
Flash loan attacks could manipulate staking token price or drain rewards
Staking token price swings could make claimFees
profitable for arbitrage at unintended times
An integrated lending market could allow stakers to over-leverage positions
Economic and Market Considerations
Liquidity Risks
Insufficient staking token liquidity could prevent withdrawals
Low rewards token reserves could cause distribution issues
Incentive Misalignment
Governance Risks
Centralization
Highly centralized admin control poses risks of manipulation
No DAO involvement in governance decisions
Lack of Adaptability
No built-in governance mechanics for parameters or logic changes
Would require admin intervention to pause or upgrade contracts
Mitigation Recommendations
Implement DAO governance and on-chain voting around critical parameters
Run simulations of different staking token market conditions and liquidity scenarios
Consider formal verification of core staking incentive mechanics
Build a robust set of circuit breakers and emergency controls
Evaluate integrating a lending market directly rather than allowing unforeseen composability
Audit all interacting DeFi protocols like Uniswap V3 contracts
Dependency Analysis
Oracles
Other Contracts
UniStaker
depends on REWARD_TOKEN
and STAKE_TOKEN
V3FactoryOwner
depends heavily on Uniswap V3 contracts
No analysis done on security/trustworthiness of these contracts
Bug or exploit in a dependency could propagate
Composability Risks
Unforeseen Interactions
Arbitrageurs could exploit claimFees
in combination with DEX trades
Flash loans could manipulate staking token price
An integrated lending market could allow dangerous overleveraging
Flash Loan Vulnerability
"Sandwich" Attacks
stake
/withdraw
calls could siphon value via frontrunningCross-chain Risks
No apparent cross-chain interactions
Mitigation Recommendations
Perform audits on all external contract dependencies
Implement circuit breakers to halt operations if anomalies detected
Use decentralized and manipulation-resistant oracles if possible
Defensively code all external interactions for reentrancy and frontrunning
Stress test composability scenarios like flash loan attacks
Build a set of monitored invariant checks using chainlink data feeds
Evaluate bridge mechanisms if any cross-chain interactions added
Category | Description |
---|---|
Security Vulnerabilities | |
Common Exploits | |
No proper validation of reward amounts in UniStaker , risking integer overflow | |
claimFees in V3FactoryOwner has no reentrancy guards | |
Timestamp manipulation could extend or shorten reward streams in UniStaker | |
Setting _rewardEndTime explicitly could frontrun and steal rewards | |
Logic Errors | |
Incorrect accumulator check in notifyRewardAmount can miss pending unclaimed rewards | |
Rounding errors possible in reward rate calculation math | |
Access Control | |
No protection on admin functions besides basic address checks | |
Any approved contract can drain rewards via notifyRewardAmount | |
Code Optimization | |
Gas Inefficiencies | |
rewardPerTokenAccumulated unnecessarily recalculates at every call | |
Iterating through reward history in lastTimeRewardApplicable is inefficient | |
Redundancies | |
lastTimeRewardApplicable duplicates logic of rewardPerTokenAccumulated | |
Helper methods could reduce redundant address sanitization checks | |
Best Practices and Standards | |
Follows Solidity naming conventions for most functions and variables | |
Insufficient comments for understandability | |
No ERC20 usage to standardize staking and reward tokens | |
Limited test cases - lacks expected failure testing | |
Mitigation Recommendations | |
Use SafeMath libraries to prevent over/underflows | |
Add reentrancy and anti-frontrunning guards | |
Introduce role-based access control schemes | |
Cache accumulator values to optimize gas costs | |
Refactor redundant logic into reusable isolated functions | |
Adhere to NatSpec and other commenting standards | |
Expand test coverage to validate all edge cases |
39 hours
#0 - c4-judge
2024-03-14T18:23:19Z
MarioPoneder marked the issue as grade-b