Omniscia KlimaDAO Audit
KlimaStakingDistributor_v4 Manual Review Findings
KlimaStakingDistributor_v4 Manual Review Findings
KSD-01M: Improper Accumulation of Rewards
| Type | Severity | Location |
|---|---|---|
| Logical Fault | Medium | KlimaStakingDistributor_v4.sol:L448-L453 |
Description:
The nextRewardFor function does not properly accumulate rewards if multiple ones are specified for a particular recipient.
Example:
446function nextRewardFor( address _recipient ) public view returns ( uint ) {447 uint reward;448 for ( uint i = 0; i < info.length; i++ ) {449 if ( info[ i ].recipient == _recipient ) {450 reward = nextRewardAt( info[ i ].rate );451 }452 }453 return reward;454}Recommendation:
We advise the function to properly sum the results of nextRewardAt invocations to ensure it operates as intended.
Alleviation:
The KlimaDAO team responded by stating that there is only one address that receives a reward. In such a case, we advise the reward to be returned directly instead to optimize the codebase even further.
KSD-02M: Improper Policy Renouncation
| Type | Severity | Location |
|---|---|---|
| Logical Fault | Medium | KlimaStakingDistributor_v4.sol:L312-L315 |
Description:
The renouncePolicy function does not properly clear out any pending _newPolicy, thus permitting a malicious policy holder to fake renouncing their rights and then reclaim them at a later date.
Example:
312function renouncePolicy() public virtual override onlyPolicy() {313 emit OwnershipTransferred( _policy, address(0) );314 _policy = address(0);315}316
317function pushPolicy( address newPolicy_ ) public virtual override onlyPolicy() {318 require( newPolicy_ != address(0), "Ownable: new owner is the zero address");319 _newPolicy = newPolicy_;320}321
322function pullPolicy() public virtual override {323 require( msg.sender == _newPolicy );324 emit OwnershipTransferred( _policy, _newPolicy );325 _policy = _newPolicy;326}Recommendation:
We advise the function to properly clear out the _newPolicy data entry.
Alleviation:
The _newPolicy entry is now properly cleared out when renouncing thereby alleviating this exhibit.
KSD-03M: Ungraceful Handling of High Adjustment Rates
| Type | Severity | Location |
|---|---|---|
| Mathematical Operations | Medium | KlimaStakingDistributor_v4.sol:L420 |
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:
419} else { // if rate should decrease420 info[ _index ].rate = info[ _index ].rate.sub( adjustment.rate ); // lower rate421 if ( info[ _index ].rate <= adjustment.target ) { // if target met422 adjustments[ _index ].rate = 0; // turn off adjustment423 }424}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 KlimaDAO team responded by stating that this is by design and a controlled contract parameter and as such no graceful handling is necessary.
KSD-04M: Inexistent Validation of Entry Validity
| Type | Severity | Location |
|---|---|---|
| Input Sanitization | Minor | KlimaStakingDistributor_v4.sol:L478-L482, L491-L497 |
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:
473/**474 @notice removes recipient for distributions475 @param _index uint476 @param _recipient address477 */478function removeRecipient( uint _index, address _recipient ) external onlyPolicy() {479 require( _recipient == info[ _index ].recipient );480 info[ _index ].recipient = address(0);481 info[ _index ].rate = 0;482}483
484/**485 @notice set adjustment info for a collector's reward rate486 @param _index uint487 @param _add bool488 @param _rate uint489 @param _target uint490 */491function setAdjustment( uint _index, bool _add, uint _rate, uint _target ) external onlyPolicy() {492 adjustments[ _index ] = Adjust({493 add: _add,494 rate: _rate,495 target: _target496 });497}Recommendation:
We advise both functions to properly validate that an info entry exists by evaluating its recipient.
Alleviation:
Both functions now properly validate that a recipient exists in the particular _index.
KSD-05M: Inexistent Validation of Reward Rate
| Type | Severity | Location |
|---|---|---|
| Input Sanitization | Minor | KlimaStakingDistributor_v4.sol:L460-L471 |
Description:
The addRecipient function does not validate that the _rewardRate set does not exceed the maximum achievable which is 1000000.
Example:
460/**461 @notice adds recipient for distributions462 @param _recipient address463 @param _rewardRate uint464 */465function addRecipient( address _recipient, uint _rewardRate ) external onlyPolicy() {466 require( _recipient != address(0) );467 info.push( Info({468 recipient: _recipient,469 rate: _rewardRate470 }));471}Recommendation:
We advise such validation to be imposed to prevent arbitrarily high reward rates.
Alleviation:
The KlimaDAO team considered this exhibit but opted to retain the codebase in its current state.