Omniscia Olympus DAO Audit
StakingDistributor Manual Review Findings
StakingDistributor Manual Review Findings
SDR-01M: Improper Accumulation of Rewards
Type | Severity | Location |
---|---|---|
Logical Fault | Medium | StakingDistributor.sol:L110-L123 |
Description:
The nextRewardFor
function does not properly accumulate rewards if multiple ones are specified for a particular recipient.
Example:
110/**111 @notice view function for next reward for specified address112 @param _recipient address113 @return uint114 */115function nextRewardFor(address _recipient) public view returns (uint256) {116 uint256 reward;117 for (uint256 i = 0; i < info.length; i++) {118 if (info[i].recipient == _recipient) {119 reward = nextRewardAt(info[i].rate);120 }121 }122 return reward;123}
Recommendation:
We advise the function to properly sum the results of nextRewardAt
invocations to ensure it operates as intended.
Alleviation:
Rewards are now properly accumulated in the reward
entry.
SDR-02M: Ungraceful Handling of High Adjustment Rates
Type | Severity | Location |
---|---|---|
Mathematical Operations | Medium | StakingDistributor.sol:L90 |
Description:
The adjustment.rate
is meant to represent a step-by-step reduction or increase of the reward rate for a particular recipient, however, there can be a case where the info[_index].rate
is smaller than the step which would render the adjust
operation impossible and thus cause the full distribute
hook to fail.
Example:
88} else {89 // if rate should decrease90 info[_index].rate = info[_index].rate.sub(adjustment.rate); // lower rate91 if (info[_index].rate <= adjustment.target) {92 // if target met93 adjustments[_index].rate = 0; // turn off adjustment94 }95}
Recommendation:
We advise the reduction of a particular rate to be gracefully handled whereby if the reduction is greater than the current rate the rate should be set to zero.
Alleviation:
The Olympus DAO team considered this exhibit but decided to retain the current behaviour of the code in place.
SDR-03M: Inexistent Validation of Entry Validity
Type | Severity | Location |
---|---|---|
Input Sanitization | Minor | StakingDistributor.sol:L142-L147, L156-L169 |
Description:
The removeRecipient
and setAdjustment
functions do not actually validate whether there is an existing entry in the _index
they are operating in, leading to adjustments for inexistent entries / future ones or removal of inexistent entries.
Example:
137/**138 @notice removes recipient for distributions139 @param _index uint140 @param _recipient address141 */142function removeRecipient(uint256 _index, address _recipient) external {143 require(msg.sender == governor() || msg.sender == guardian(), "Caller is not governor or guardian");144 require(_recipient == info[_index].recipient);145 info[_index].recipient = address(0);146 info[_index].rate = 0;147}148
149/**150 @notice set adjustment info for a collector's reward rate151 @param _index uint152 @param _add bool153 @param _rate uint154 @param _target uint155 */156function setAdjustment(157 uint256 _index,158 bool _add,159 uint256 _rate,160 uint256 _target161) external {162 require(msg.sender == governor() || msg.sender == guardian(), "Caller is not governor or guardian");163
164 if (msg.sender == guardian()) {165 require(_rate <= info[_index].rate.mul(25).div(1000), "Limiter: cannot adjust by >2.5%");166 }167
168 adjustments[_index] = Adjust({add: _add, rate: _rate, target: _target});169}
Recommendation:
We advise both functions to properly validate that an info
entry exists by evaluating its recipient
.
Alleviation:
Both functions now properly validate the existance of an entry by ensuring that the info[_index].recipient
member is non-zero.
SDR-04M: Inexistent Validation of Reward Rate
Type | Severity | Location |
---|---|---|
Input Sanitization | Minor | StakingDistributor.sol:L134 |
Description:
The addRecipient
function does not validate that the _rewardRate
set does not exceed the maximum achievable which is 1000000
.
Example:
127/**128 @notice adds recipient for distributions129 @param _recipient address130 @param _rewardRate uint131 */132function addRecipient(address _recipient, uint256 _rewardRate) external onlyGovernor {133 require(_recipient != address(0));134 info.push(Info({recipient: _recipient, rate: _rewardRate}));135}
Recommendation:
We advise such validation to be imposed to prevent arbitrarily high reward rates.
Alleviation:
The function now properly validate that the _rewardRate
set is at most equivalent to the newly declared rateDenominator
.