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.