Platform: Code4rena
Start Date: 20/09/2022
Pot Size: $30,000 USDC
Total HM: 12
Participants: 198
Period: 3 days
Judge: 0xean
Total Solo HM: 2
Id: 164
League: ETH
Rank: 92/198
Findings: 2
Award: $28.01
π Selected for report: 0
π Solo Findings: 0
π Selected for report: AkshaySrivastav
Also found by: 0v3rf10w, 0x040, 0x1f8b, 0x4non, 0x5rings, 0x85102, 0xA5DF, 0xDecorativePineapple, 0xNazgul, 0xSky, 0xSmartContract, 0xbepresent, 0xf15ers, 0xmatt, 2997ms, Aeros, Aymen0909, B2, Bahurum, Bnke0x0, CertoraInc, Chom, ChristianKuri, CodingNameKiki, Deivitto, Diana, Diraco, Dravee, ElKu, Funen, IllIllI, JC, JLevick, JohnSmith, JohnnyTime, KIntern_NA, Lambda, Margaret, MasterCookie, OptimismSec, RaymondFam, Respx, ReyAdmirado, RockingMiles, Rohan16, Rolezn, Ruhum, RustyRabbit, Sm4rty, SooYa, StevenL, TomJ, Tomo, V_B, Waze, Yiko, __141345__, a12jmx, ajtra, ak1, async, ayeslick, aysha, berndartmueller, bin2chen, bobirichman, brgltd, bulej93, c3phas, carrotsmuggler, cccz, ch13fd357r0y3r, chatch, cryptostellar5, cryptphi, csanuragjain, d3e4, datapunk, delfin454000, dic0de, djxploit, durianSausage, eighty, erictee, exd0tpy, fatherOfBlocks, gogo, got_targ, hansfriese, ignacio, ikbkln, indijanc, innertia, joestakey, karanctf, ladboy233, leosathya, lukris02, martin, medikko, millersplanet, nalus, natzuu, neko_nyaa, neumo, obront, oyc_109, pcarranzav, peanuts, pedr02b2, pedroais, peiw, peritoflores, prasantgupta52, rajatbeladiya, rbserver, reassor, ret2basic, rokinot, romand, rotcivegaf, rvierdiiev, sach1r0, seyni, sikorico, slowmoses, sorrynotsorry, supernova, tibthecat, tnevler, ubermensch, yongskiws, zzykxx, zzzitron
18.9219 USDC - $18.92
@param
statementsFullPremintERC20Token.sol: L8-14
contract FullPremintERC20Token is ERC20 { // uint constant _initialSupply = 100 * (10**18); constructor(string memory name_, string memory symbol_, uint256 supply_) ERC20(name_, symbol_) { require(supply_ > 0, "NO_ZERO_MINT"); _mint(_msgSender(), supply_); } }
@param
statements missing for name_
and symbol_
In theory, comments over 79 characters should wrap using multi-line comment syntax. Even if somewhat longer comments are acceptable and a scroll bar is provided, there are cases where very long comments interfere with readability. Below are five instances of extra-long comments whose readability could be improved by wrapping, as shown:
@notice Permission-unchecked version of claim creation (no onlyAdmin). Actual logic for create claim, to be run within either createClaim or createClaimBatch.
Suggestion:
@notice Permission-unchecked version of claim creation (no onlyAdmin). Actual logic for create claim, to be run within either createClaim or createClaimBatch.
The following line occurs twice:
@param _releaseIntervalSecs - The release interval for the linear vesting. If this is, for example, 60, that means that the linearly vested amount gets released every 60 seconds.
Suggestion:
@param _releaseIntervalSecs - The release interval for the linear vesting. If this is, for example, 60, that means that the linearly vested amount gets released every 60 seconds.
// -> Conclusion: we want to allow this, for founders that might have forgotten to add some users, or to avoid issues with transactions not going through because of discoordination between block.timestamp and sender's local time
Suggestion:
// -> Conclusion: we want to allow this, for founders that might have forgotten // to add some users, or to avoid issues with transactions not going through // because of discoordination between block.timestamp and sender's local time.
// Both or neither of _cliffReleaseTimestamp and _cliffAmount must be set. If cliff is set, _cliffReleaseTimestamp must be before or at the _startTimestamp
Suggestion:
// Both or neither of _cliffReleaseTimestamp and _cliffAmount must be set. If cliff // is set, _cliffReleaseTimestamp must be before or at the _startTimestamp.
// No need for separate emit, since createClaim will emit for each claim (and this function is merely a convenience/gas-saver for multiple claims creation)
Suggestion:
// No need for separate emit, since createClaim will emit for each claim (and this // function is merely a convenience/gas-saver for multiple claims creation).
Comments that contain TODOs or other language referring to open items should be addressed and modified or removed. Below are two instances:
VariableSupplyERC20Token.sol: L26
// Should we allow this?
// Potential TODO: sanity check, if _linearVestAmount == 0, should we perhaps force that start and end ts are the same?
For readability, scientific notation for large multiples of ten is preferable to using exponentiation
// uint constant _initialSupply = 100 * (10**18);
Suggestion: Change 10**18
to 1e18
π Selected for report: IllIllI
Also found by: 0v3rf10w, 0x040, 0x1f8b, 0x4non, 0x85102, 0xA5DF, 0xDanielC, 0xNazgul, 0xSmartContract, 0xbepresent, 0xc0ffEE, 0xsam, 2997ms, AkshaySrivastav, Amithuddar, Atarpara, Aymen0909, B2, Bnke0x0, CertoraInc, Chom, ChristianKuri, CodingNameKiki, Deivitto, Diana, DimitarDimitrov, Diraco, Funen, JC, JLevick, JohnSmith, Junnon, KIntern_NA, Lambda, MasterCookie, Matin, Noah3o6, Ocean_Sky, OptimismSec, RaymondFam, Respx, ReyAdmirado, RockingMiles, Rohan16, Rolezn, Ruhum, Saintcode_, Satyam_Sharma, Sm4rty, SnowMan, SooYa, Sta1400, StevenL, Tadashi, Tagir2003, TomJ, Tomio, Tomo, V_B, Waze, WilliamAmbrozic, Yiko, __141345__, a12jmx, adriro, ajtra, ak1, async, aysha, beardofginger, bobirichman, brgltd, bulej93, c3phas, carrotsmuggler, caventa, ch0bu, cryptostellar5, cryptphi, csanuragjain, d3e4, delfin454000, dharma09, djxploit, durianSausage, eighty, emrekocak, erictee, exd0tpy, fatherOfBlocks, francoHacker, gianganhnguyen, gogo, got_targ, hxzy, ignacio, ikbkln, imare, indijanc, jag, jpserrat, karanctf, ladboy233, leosathya, lucacez, lukris02, m9800, malinariy, martin, medikko, mics, millersplanet, mrpathfindr, nalus, natzuu, neko_nyaa, oyc_109, pauliax, peanuts, pedroais, peiw, pfapostol, prasantgupta52, rbserver, ret2basic, rokinot, rotcivegaf, rvierdiiev, sach1r0, samruna, seyni, slowmoses, subtle77, supernova, tgolding55, tibthecat, tnevler, w0Lfrum, yaemsobak, zishansami
9.0866 USDC - $9.09
require
functionSplitting into separate require()
statements saves gas
VTVLVesting.sol: L344-351
require(_startTimestamps.length == length && _endTimestamps.length == length && _cliffReleaseTimestamps.length == length && _releaseIntervalsSecs.length == length && _linearVestAmounts.length == length && _cliffAmounts.length == length, "ARRAY_LENGTH_MISMATCH" );
Recommendation:
require(_startTimestamps.length == length, "ARRAY_LENGTH_MISMATCH"; require(_endTimestamps.length == length, "ARRAY_LENGTH_MISMATCH"; require(_cliffReleaseTimestamps.length == length, "ARRAY_LENGTH_MISMATCH"; require(_releaseIntervalsSecs.length == length, "ARRAY_LENGTH_MISMATCH"; require(_linearVestAmounts.length == length, "ARRAY_LENGTH_MISMATCH"; require(_cliffAmounts.length == length, "ARRAY_LENGTH_MISMATCH";
!= 0
instead of > 0
in a require
statement if variable is an unsigned integer (uint
)!= 0
should be used where possible since > 0
costs more gas
FullPremintERC20Token.sol: L8-14
contract FullPremintERC20Token is ERC20 { // uint constant _initialSupply = 100 * (10**18); constructor(string memory name_, string memory symbol_, uint256 supply_) ERC20(name_, symbol_) { require(supply_ > 0, "NO_ZERO_MINT"); _mint(_msgSender(), supply_); } }
Recommendation: Change supply_ > 0
to supply != 0
Initializing uint
variables to their default value of 0
is unnecessary and costs gas
uint112 public numTokensReservedForVesting = 0;
Change to:
uint112 public numTokensReservedForVesting;
uint112 vestAmt = 0;
Change to:
uint112 vestAmt;
++i
instead of i++
to increase count in a for
loopSince use of i++
(or equivalent counter) costs more gas, it is better to use ++i
for (uint256 i = 0; i < length; i++) { _createClaimUnchecked(_recipients[i], _startTimestamps[i], _endTimestamps[i], _cliffReleaseTimestamps[i], _releaseIntervalsSecs[i], _linearVestAmounts[i], _cliffAmounts[i]); }
Suggestion:
for (uint256 i = 0; i < length; ++i) { _createClaimUnchecked(_recipients[i], _startTimestamps[i], _endTimestamps[i], _cliffReleaseTimestamps[i], _releaseIntervalsSecs[i], _linearVestAmounts[i], _cliffAmounts[i]); }
It costs less gas to inline the code of functions that are only called once. Doing so saves the cost of allocating the function selector, and the cost of the jump when the function is called.
modifier hasNoClaim(address _recipient) { Claim storage _claim = claims[_recipient]; // Start timestamp != 0 is a sufficient condition for a claim to exist // This is because we only ever add claims (or modify startTs) in the createClaim function // Which requires that its input startTimestamp be nonzero // So therefore, a zero value for this indicates the claim does not exist. require(_claim.startTimestamp == 0, "CLAIM_ALREADY_EXISTS"); // We don't even need to check for active to be unset, since this function only // determines that a claim hasn't been set // require(_claim.isActive == false, "CLAIM_ALREADY_EXISTS"); // Further checks aren't necessary (to save gas), as they're done at creation time (createClaim) // require(_claim.endTimestamp == 0, "CLAIM_ALREADY_EXISTS"); // require(_claim.linearVestAmount + _claim.cliffAmount == 0, "CLAIM_ALREADY_EXISTS"); // require(_claim.amountWithdrawn == 0, "CLAIM_ALREADY_EXISTS"); _; }
Since hasNoClaim()
is used only once in this contract (in function _createClaimUnchecked()
) as shown below, it should be inlined to save gas
function _createClaimUnchecked( address _recipient, uint40 _startTimestamp, uint40 _endTimestamp, uint40 _cliffReleaseTimestamp, uint40 _releaseIntervalSecs, uint112 _linearVestAmount, uint112 _cliffAmount ) private hasNoClaim(_recipient) {