Platform: Code4rena
Start Date: 27/10/2022
Pot Size: $33,500 USDC
Total HM: 8
Participants: 96
Period: 3 days
Judge: kirk-baird
Total Solo HM: 1
Id: 176
League: ETH
Rank: 40/96
Findings: 2
Award: $31.16
π Selected for report: 0
π Solo Findings: 0
π Selected for report: robee
Also found by: 0x007, 0x1f8b, 0x52, 0xDjango, 0xNazgul, 0xSmartContract, 8olidity, Awesome, B2, Bnke0x0, Chom, Diana, Dravee, JTJabba, Jeiwan, Josiah, Lambda, Mathieu, Picodes, RaoulSchaffranek, RaymondFam, RedOneN, ReyAdmirado, Rolezn, Ruhum, Sm4rty, Tricko, Trust, Waze, __141345__, a12jmx, adriro, ajtra, brgltd, c3phas, carlitox477, cccz, ch0bu, chaduke, chrisdior4, corerouter, cryptonue, csanuragjain, ctf_sec, cylzxje, delfin454000, dic0de, djxploit, horsefacts, imare, jayphbee, jwood, ktg, ladboy233, leosathya, lukris02, minhtrng, neko_nyaa, oyc_109, pashov, peritoflores, rbserver, rvierdiiev, shark, tnevler, yixxas
19.6449 USDC - $19.64
Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.
Instances number of this issue: 6
85-92 event NewPledge( address creator, address receiver, address rewardToken, uint256 targetVotes, uint256 rewardPerVote, uint256 endTimestamp ); 94 event ExtendPledgeDuration(uint256 indexed pledgeId, uint256 oldEndTimestamp, uint256 newEndTimestamp); 96 event IncreasePledgeTargetVotes(uint256 indexed pledgeId, uint256 oldTargetVotes, uint256 newTargetVotes); 98 event IncreasePledgeRewardPerVote(uint256 indexed pledgeId, uint256 oldRewardPerVote, uint256 newRewardPerVote); 102 event RetrievedPledgeRewards(uint256 indexed pledgeId, address receiver, uint256 amount); 105 event Pledged(uint256 indexed pledgeId, address indexed user, uint256 amount, uint256 endTimestamp);
Description: Setter functions are missing checks to validate if the new value being set is the same as the current value already set in the contract. Such checks will showcase mismatches between on-chain and off-chain states.
Suggestion: This may hinder detecting discrepancies between on-chain and off-chain states leading to flawed assumptions of on-chain state and protocol behavior.
Instances number of this issue: 3
function updateChest(address chest) external onlyOwner { if(chest == address(0)) revert Errors.ZeroAddress(); address oldChest = chestAddress; chestAddress = chest; emit ChestUpdated(oldChest, chest); } function updateMinTargetVotes(uint256 newMinTargetVotes) external onlyOwner { if(newMinTargetVotes == 0) revert Errors.InvalidValue(); uint256 oldMinTarget = minTargetVotes; minTargetVotes = newMinTargetVotes; emit MinTargetUpdated(oldMinTarget, newMinTargetVotes); } function updatePlatformFee(uint256 newFee) external onlyOwner { if(newFee > 500) revert Errors.InvalidValue(); uint256 oldfee = protocalFeeRatio; protocalFeeRatio = newFee; emit PlatformFeeUpdated(oldfee, newFee); }
When critical parameters of systems need to be changed, it is required to broadcast the change via event emission and recommended to enforce the changes after a time-delay. This is to allow system users to be aware of such critical changes and give them an opportunity to exit or adjust their engagement with the system accordingly. None of the onlyOwner functions that change critical protocol addresses/parameters have a timelock for a time-delayed change to alert:
Instances number of this issue: 3
function updateChest(address chest) external onlyOwner { if(chest == address(0)) revert Errors.ZeroAddress(); address oldChest = chestAddress; chestAddress = chest; emit ChestUpdated(oldChest, chest); } function updateMinTargetVotes(uint256 newMinTargetVotes) external onlyOwner { if(newMinTargetVotes == 0) revert Errors.InvalidValue(); uint256 oldMinTarget = minTargetVotes; minTargetVotes = newMinTargetVotes; emit MinTargetUpdated(oldMinTarget, newMinTargetVotes); } function updatePlatformFee(uint256 newFee) external onlyOwner { if(newFee > 500) revert Errors.InvalidValue(); uint256 oldfee = protocalFeeRatio; protocalFeeRatio = newFee; emit PlatformFeeUpdated(oldfee, newFee); }
Suggestion: Users may be surprised when critical parameters are changed. Furthermore, it can erode users' trust since they canβt be sure the protocol rules wonβt be changed later on. Compromised owner keys may be used to change protocol addresses/parameters to benefit attackers. Without a time-delay, authorized owners have no time for any planned incident response. (May be medium since owner can change critical parameters at anytime that can affect the users poorly).
#0 - c4-judge
2022-11-12T00:28:37Z
kirk-baird marked the issue as grade-b
π Selected for report: c3phas
Also found by: 0x1f8b, 0xNazgul, 0xRoxas, 0xSmartContract, 0xbepresent, Amithuddar, Awesome, B2, Bnke0x0, Dravee, KoKo, Mathieu, Picodes, RaymondFam, RedOneN, ReyAdmirado, RockingMiles, Ruhum, SadBase, SooYa, Waze, __141345__, adriro, ajtra, ballx, carlitox477, ch0bu, cylzxje, djxploit, durianSausage, emrekocak, erictee, gogo, halden, horsefacts, imare, indijanc, karanctf, leosathya, lukris02, neko_nyaa, oyc_109, peiw, sakman, shark, skyle, tnevler
11.5153 USDC - $11.52
X = X + Y / X = X - Y
IS CHEAPER THAN X += Y / X -= Y
The demo of the gas comparison can be seen here.
Consider use X = X + Y / X = X - Y
to save gas.
Instances number of this issue:
268 pledgeAvailableRewardAmounts[pledgeId] -= rewardAmount; 340 pledgeAvailableRewardAmounts[vars.newPledgeID] += vars.totalRewardAmount; 401 pledgeAvailableRewardAmounts[pledgeId] += totalRewardAmount; 445 pledgeAvailableRewardAmounts[pledgeId] += totalRewardAmount;
For example, to update the num
variable with newVal
, the current way is as following:
uint oldVal = num; num = newVal; emit update(oldVal, newVal);
If the execution order is adjusted, some operations can be saved (memory space allocation, variable assignment), reducing both the deployment and run time gas cost.
emit update(num, newVal); num = newVal;
The operation saved: 1 sload, 1 memory allocation, 1 mstore.
The demo of the gas comparison can be seen here.
There are multiple places can use this trick for optimization, since the updates of parameters are widely and frequently used, the optimization can be beneficial.
Instances number of this issue: 3
function updateChest(address chest) external onlyOwner { if(chest == address(0)) revert Errors.ZeroAddress(); address oldChest = chestAddress; chestAddress = chest; emit ChestUpdated(oldChest, chest); } function updateMinTargetVotes(uint256 newMinTargetVotes) external onlyOwner { if(newMinTargetVotes == 0) revert Errors.InvalidValue(); uint256 oldMinTarget = minTargetVotes; minTargetVotes = newMinTargetVotes; emit MinTargetUpdated(oldMinTarget, newMinTargetVotes); } function updatePlatformFee(uint256 newFee) external onlyOwner { if(newFee > 500) revert Errors.InvalidValue(); uint256 oldfee = protocalFeeRatio; protocalFeeRatio = newFee; emit PlatformFeeUpdated(oldfee, newFee); }
Some function codes overlap. Consider reuse the common part and save deployment gas.
retrievePledgeRewards()
and closePledge()
codes overlap a lot, only a few lines of difference.
the following are used in extendPledge()
, increasePledgeRewardPerVote()
and _pledge()
:
Pledge storage pledgeParams = pledges[pledgeId]; if(pledgeParams.closed) revert Errors.PledgeClosed(); if(pledgeParams.endTimestamp <= block.timestamp) revert Errors.ExpiredPledge();
pledgeParams.closed
could be deletedThis variable does not seem useful, since the check for if(pledgeParams.endTimestamp <= block.timestamp)
can serve the purpose of pledgeParams.closed
.
Consider delete pledgeParams.closed
.
#0 - c4-judge
2022-11-12T00:28:00Z
kirk-baird marked the issue as grade-b