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: 93/198
Findings: 2
Award: $27.97
π 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.8574 USDC - $18.86
TODO's should be removed before the deployment of a contracts where this potential TODO is not a big concern but should be renmoved.Tagging the line here
contracts/VTVLVesting.sol:266: // Potential TODO: sanity check, if _linearVestAmount == 0, should we perhaps force that start and end ts are the same?
//WRITTEN CODE IN PROGRAM
require((_endTimestamp - _startTimestamp) % _releaseIntervalSecs == 0, "INVALID_INTERVAL_LENGTH"); // Potential TODO: sanity check, if _linearVestAmount == 0, should we perhaps force that start and end ts are the same? // No point in allowing cliff TS without the cliff amount or vice versa. // Both or neither of _cliffReleaseTimestamp and _cliffAmount must be set. If cliff is set, _cliffReleaseTimestamp must be before or at the _startTimestamp require( (
Before the deployment of contracts removal of TODOs make it professional.
Codes should be given in the main line instead of comment.
contracts/VTVLVesting.sol:114: // require(_claim.linearVestAmount + _claim.cliffAmount > 0, "INVALID_VESTED_AMOUNT"); contracts/VTVLVesting.sol:115: // require(_claim.endTimestamp > 0, "NO_END_TIMESTAMP"); contracts/VTVLVesting.sol:261: // require(_endTimestamp > 0, "_endTimestamp must be valid"); // not necessary because of the next condition (transitively)
Use a solidity version of at least 0.8.13 to get the ability to use using for
with a list of free functions
contracts/test/TestERC20Token.sol:2:pragma solidity ^0.8.0;
π 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.1106 USDC - $9.11
contracts/token/FullPremintERC20Token.sol:11: require(supply_ > 0, "NO_ZERO_MINT");
contracts/token/VariableSupplyERC20Token.sol:27: require(initialSupply_ > 0 || maxSupply_ > 0, "INVALID_AMOUNT");
contracts/VTVLVesting.sol:107: require(_claim.startTimestamp > 0, "NO_ACTIVE_CLAIM"); contracts/VTVLVesting.sol:256: require(_linearVestAmount + _cliffAmount > 0, "INVALID_VESTED_AMOUNT"); // Actually only one of linearvested/cliff amount must be 0, not necessarily both contracts/VTVLVesting.sol:257: require(_startTimestamp > 0, "INVALID_START_TIMESTAMP"); contracts/VTVLVesting.sol:263: require(_releaseIntervalSecs > 0, "INVALID_RELEASE_INTERVAL"); contracts/VTVLVesting.sol:272: _cliffReleaseTimestamp > 0 && contracts/VTVLVesting.sol:273: _cliffAmount > 0 && contracts/VTVLVesting.sol:449: require(bal > 0, "INSUFFICIENT_BALANCE");
Remove pragma solidity version
contracts/test/TestERC20Token.sol:2:pragma solidity ^0.8.0; contracts/token/VariableSupplyERC20Token.sol:2:pragma solidity ^0.8.14;
Pre-increments and pre-decrements are cheaper.
For a uint256
i variable, the following is true with the Optimizer enabled at 10k:
Increment:
i += 1
is the most expensive form
i++
costs 6
gas
less than i += 1
++i
costs 5 gas
less than i++
(11 gas less than i += 1)
Note that post-increments (or post-decrements) return the old value before incrementing or decrementing, hence the name post-increment:
VTVLVesting.sol:353: for (uint256 i = 0; i < length; i++) {
The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas
VTVLVesting.sol:353: for (uint256 i = 0; i < length; i++) {
Require statements including conditions with the && operator can be broken down in multiple require statements to save gas.
VTVLVesting.sol:344: require(_startTimestamps.length == length &&
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met). Starting from Solidity v0.8.4,there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");),but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them. Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
AccessProtected.sol:25: require(_admins[_msgSender()], "ADMIN_ACCESS_REQUIRED"); AccessProtected.sol:40: require(admin != address(0), "INVALID_ADDRESS"); VTVLVesting.sol:82: require(address(_tokenAddress) != address(0), "INVALID_ADDRESS"); VTVLVesting.sol:107: require(_claim.startTimestamp > 0, "NO_ACTIVE_CLAIM"); VTVLVesting.sol:111: require(_claim.isActive == true, "NO_ACTIVE_CLAIM"); VTVLVesting.sol:129: require(_claim.startTimestamp == 0, "CLAIM_ALREADY_EXISTS"); VTVLVesting.sol:255: require(_recipient != address(0), "INVALID_ADDRESS"); VTVLVesting.sol:256: require(_linearVestAmount + _cliffAmount > 0, "INVALID_VESTED_AMOUNT"); // Actually only one of linearvested/cliff amount must be 0, not necessarily both VTVLVesting.sol:257: require(_startTimestamp > 0, "INVALID_START_TIMESTAMP"); VTVLVesting.sol:262: require(_startTimestamp < _endTimestamp, "INVALID_END_TIMESTAMP"); // _endTimestamp must be after _startTimestamp VTVLVesting.sol:263: require(_releaseIntervalSecs > 0, "INVALID_RELEASE_INTERVAL"); VTVLVesting.sol:264: require((_endTimestamp - _startTimestamp) % _releaseIntervalSecs == 0, "INVALID_INTERVAL_LENGTH"); VTVLVesting.sol:295: require(tokenAddress.balanceOf(address(this)) >= numTokensReservedForVesting + allocatedAmount, "INSUFFICIENT_BALANCE"); VTVLVesting.sol:374: require(allowance > usrClaim.amountWithdrawn, "NOTHING_TO_WITHDRAW"); VTVLVesting.sol:402: require(amountRemaining >= _amountRequested, "INSUFFICIENT_BALANCE"); VTVLVesting.sol:426: require( _claim.amountWithdrawn < finalVestAmt, "NO_UNVESTED_AMOUNT"); VTVLVesting.sol:447: require(_otherTokenAddress != tokenAddress, "INVALID_TOKEN"); // tokenAddress address is already sure to be nonzero due to constructor VTVLVesting.sol:449: require(bal > 0, "INSUFFICIENT_BALANCE"); token/VariableSupplyERC20Token.sol:27: require(initialSupply_ > 0 || maxSupply_ > 0, "INVALID_AMOUNT"); token/VariableSupplyERC20Token.sol:37: require(account != address(0), "INVALID_ADDRESS"); token/VariableSupplyERC20Token.sol:41: require(amount <= mintableSupply, "INVALID_AMOUNT"); token/FullPremintERC20Token.sol:11: require(supply_ > 0, "NO_ZERO_MINT");
token/FullPremintERC20Token.sol:9: // uint constant _initialSupply = 100 * (10**18);
10 ** 18 can be changed to 1e18 to avoid unnecessary arithmetic operation and save gas.
Comparing to a constant (true or false) is a bit more expensive than directly checking the returned boolean value. I suggest using if(directValue) instead of if(directValue == true) and if(!directValue) instead of if(directValue == false) here
VTVLVesting.sol:111: require(_claim.isActive == true, "NO_ACTIVE_CLAIM");
VTVLVesting.sol:161: vestAmt += _claim.cliffAmount; VTVLVesting.sol:179: vestAmt += linearVestAmount; VTVLVesting.sol:301: numTokensReservedForVesting += allocatedAmount; // track the allocated amount VTVLVesting.sol:381: usrClaim.amountWithdrawn += amountRemaining; VTVLVesting.sol:383: numTokensReservedForVesting -= amountRemaining; VTVLVesting.sol:433: numTokensReservedForVesting -= amountRemaining; // Reduces the allocation token/VariableSupplyERC20Token.sol:43: mintableSupply -= amount;
If a variable is not set/initialized, it is assumed to have the default value (0 for uint, false for bool, address(0) for addressβ¦). Explicitly initializing it with its default value is an anti-pattern and wastes gas.
We can use uint a;
instead of uint a = 0;
VTVLVesting.sol:27: uint112 public numTokensReservedForVesting = 0; VTVLVesting.sol:148: uint112 vestAmt = 0;