Omniscia Maverick Protocol Audit
MaverickV2IncentiveMatcher Code Style Findings
MaverickV2IncentiveMatcher Code Style Findings
MVI-01C: Generic Typographic Mistakes
| Type | Severity | Location |
|---|---|---|
| Code Style | ![]() | MaverickV2IncentiveMatcher.sol:L265, L306 |
Description:
The referenced lines contain typographical mistakes (i.e. private variable without an underscore prefix) or generic documentational errors (i.e. copy-paste) that should be corrected.
Example:
265// check is vote period is overRecommendation:
We advise them to be corrected enhancing the legibility of the codebase.
Alleviation (07ad29f773f16bdfbae3d97d3a7c2f9d64866093):
The referenced typographic mistakes in the comment statements have been corrected, addressing this exhibit.
MVI-02C: Incorrect Vetoing Start Period (Error Argument)
| Type | Severity | Location |
|---|---|---|
| Logical Fault | ![]() | MaverickV2IncentiveMatcher.sol:L267 |
Description:
The IncentiveMatcherVetoPeriodNotActive error will be yielded with an incorrect vetoStart argument as it is set to epoch.
Impact:
As the flaw is present in a revert scenario and affects the data point yielded by the emitted error, its severity is set to informational.
Example:
265// check is vote period is over266if (!vetoingIsActive(epoch)) {267 revert IncentiveMatcherVetoPeriodNotActive(block.timestamp, epoch, vetoingEnd(epoch));268}Recommendation:
We advise the epoch argument to be updated to epochEnd(epoch), properly reflecting the check performed by the MaverickV2IncentiveMatcher::vetoingIsActive function.
Alleviation (07ad29f773):
The Maverick Protocol team stated that they have remediated this exhibit in their response document, however, it remains open in the commit hash we evaluated.
Alleviation (7ad1f96f09):
The error declaration's emitted arguments have been corrected, properly signalling the veto period's start in the overall IncentiveMatcherVetoPeriodNotActive error emitted.
MVI-03C: Inefficient Voting Budget Rollover Calculation
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | MaverickV2IncentiveMatcher.sol:L545, L553 |
Description:
The referenced calculation is inefficient as, whenever it is evaluated, it will either result in 0 or the matchAmounts.voteBudget.
Example:
542if (rewardTotals.proRataProduct == 0) {543 // if there was zero pro rata product, then none of the vote budget544 // was allocated and all of it can be rolled over.545 voteRolloverAmount = checkpoint.voteBudget;546} else {547 // all vote budget was allocated548 voteRolloverAmount = 0;549}550
551// pro rate by the amount this matcher contributed to the budget552if (checkpoint.voteBudget != 0)553 voteRolloverAmount = OzMath.mulDiv(voteRolloverAmount, matchAmounts.voteBudget, checkpoint.voteBudget);Recommendation:
We advise the conditional to be removed entirely, and the voteRolloverAmount assignment that precedes it to be assigned to the matchAmounts.voteBudget directly thus optimizing the code's gas cost.
Alleviation (7ad1f96f091b2bac58f9e800b8361a1c85753854):
The vote rollover calculations have been simplified as advised, assigning the matchAmounts.voteBudget value directly to the voteRolloverAmount when the proRataProduct is 0.
MVI-04C: Inefficient mapping Lookups
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | MaverickV2IncentiveMatcher.sol:L228, L229 |
Description:
The linked statements perform key-based lookup operations on mapping declarations from storage multiple times for the same key redundantly.
Example:
228checkpoint.matcherAmounts[msg.sender].matchBudget += matchBudget;229checkpoint.matcherAmounts[msg.sender].voteBudget += voteBudget;Recommendation:
As the lookups internally perform an expensive keccak256 operation, we advise the lookups to be cached wherever possible to a single local declaration that either holds the value of the mapping in case of primitive types or holds a storage pointer to the struct contained.
As the compiler's optimizations may take care of these caching operations automatically at-times, we advise the optimization to be selectively applied, tested, and then fully adopted to ensure that the proposed caching model indeed leads to a reduction in gas costs.
Alleviation (07ad29f773f16bdfbae3d97d3a7c2f9d64866093):
The Maverick Protocol team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase
MVI-05C: Inexistent Re-Use of Local Variable
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | MaverickV2IncentiveMatcher.sol:L536, L554, L555 |
Description:
The matchBudget local variable is solely used for the matchRolloverAmount calculation even though the checkpoint.matchBudget entry is re-read at least once and at most twice after it.
Example:
532// Two steps:533// - compute total rollover amount534// - pro rate that total by the amount this sender contributed to the535// budget536uint256 matchBudget = checkpoint.matchBudget;537// roll over any unmatched. if matched pro rata, there is no match538// budget to roll over, if not, then there will be some excess unmatched539// allocation to roll over540matchRolloverAmount = Math.clip(matchBudget, _getAdjustedExternalIncentives(rewardTotals));541
542if (rewardTotals.proRataProduct == 0) {543 // if there was zero pro rata product, then none of the vote budget544 // was allocated and all of it can be rolled over.545 voteRolloverAmount = checkpoint.voteBudget;546} else {547 // all vote budget was allocated548 voteRolloverAmount = 0;549}550
551// pro rate by the amount this matcher contributed to the budget552if (checkpoint.voteBudget != 0)553 voteRolloverAmount = OzMath.mulDiv(voteRolloverAmount, matchAmounts.voteBudget, checkpoint.voteBudget);554if (checkpoint.matchBudget != 0)555 matchRolloverAmount = OzMath.mulDiv(matchRolloverAmount, matchAmounts.matchBudget, checkpoint.matchBudget);Recommendation:
We advise the ensuing checkpoint.matchBudget read operations to be replaced by the matchBudget variable directly, optimizing the code's gas cost.
Alleviation (7ad1f96f091b2bac58f9e800b8361a1c85753854):
The two instances of checkpoint.matchBudget have been properly replaced by the existing matchBudget variable, optimizing the code's gas cost.
MVI-06C: Redundant Duplicate Safe Casts
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | MaverickV2IncentiveMatcher.sol:L259, L321 |
Description:
The referenced safe SafeCast::toUint128 operations are redundant as the variable has already been safely cast in the preceding statement and is thus safe to cast without any further checks.
Example:
258currentCheckpoint.totalExternalIncentivesAdded += amount.toUint128();259currentCheckpoint.externalIncentivesByReward[rewardContract] += amount.toUint128();Recommendation:
We advise the referenced casting operations to be performed directly without the usage of the SafeCast library, optimizing the code's gas cost whilst retaining the same level of security.
Alleviation (07ad29f773f16bdfbae3d97d3a7c2f9d64866093):
The redundant duplicate safe casts have been optimized in both pairs highlighted by this exhibit, rendering it addressed.
MVI-07C: Redundant Local Variables
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | MaverickV2IncentiveMatcher.sol:L252, L416 |
Description:
The referenced local variables store immutable variables to memory redundantly, increasing the gas cost of the involved statements.
Example:
252IERC20 _baseToken = baseToken;253_baseToken.safeTransferFrom(msg.sender, address(rewardContract), amount);254duration = rewardContract.notifyRewardAmount(_baseToken, _duration);Recommendation:
We advise the local variables to be omitted, and all usages of the local variables to be replaced by direct usage of the respective immutable variables optimizing their gas cost.
Alleviation (07ad29f773f16bdfbae3d97d3a7c2f9d64866093):
The referenced local variables of immutable declarations have been properly removed, optimizing each statement that makes use of the immutable declaration directly.
MVI-08C: Redundant Unconditional Calculation of Maximum Vote Match
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | MaverickV2IncentiveMatcher.sol:L398-L402 |
Description:
The maxVoteMatch variable is calculated regardless of whether the ensuing if block which it is used in is triggered.
Example:
398uint256 maxVoteMatch = Math.mulDivFloor(399 checkpoint.voteBudget,400 checkpoint.externalIncentivesByReward[rewardContract],401 checkpoint.totalExternalIncentivesAdded402);403
404if (checkpoint.totalVote != 0) {405 uint256 totalVote = checkpoint.totalVote;406 uint256 voteMatch = Math.mulDivFloor(checkpoint.votesByReward[rewardContract], maxVoteMatch, totalVote);407 // decrement by 1 wei to "round down" since voteMatch is the result408 // of rounding down, straight subtraction could lead to be409 // voteRollover to be an implicit round up operation. But rounding410 // up must be avoided as round up operations will accumulate and411 // lead to a cumulative rollover value that is too large.412 checkpoint.voteRollover += Math.clip(maxVoteMatch - voteMatch, 1).toUint128();413
414 matchAmount += voteMatch;415}Recommendation:
We advise the maxVoteMatch declaration to be relocated within the ensuing if block, ensuring that it is calculated and its memory is reserved solely when it is expected to be utilized.
Alleviation (07ad29f773f16bdfbae3d97d3a7c2f9d64866093):
The vote matching mechanism has been significantly refactored as a result of exhibits MVI-03M and MVI-04M, rendering this optimization no longer applicable.
