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: 88/198
Findings: 2
Award: $28.18
🌟 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
Code architecture, incentives, and error handling/reporting questions/issues should be resolved before deployment
VTVLVesting.sol:266: // Potential TODO: sanity check, if _linearVestAmount == 0, should we perhaps force that start and end ts are the same?
https://code4rena.com/reports/2022-05-sturdy/#l-09-open-todos
Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively.
token/VariableSupplyERC20Token.sol:2:pragma solidity ^0.8.14;
Recommend using fixed solidity version
https://code4rena.com/reports/2022-04-phuture#g-20-use-a-more-recent-version-of-solidity
Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts.
VTVLVesting.sol:217: return _baseVestedAmount(_claim, uint40(block.timestamp)) - _claim.amountWithdrawn; VTVLVesting.sol:371: uint112 allowance = vestedAmount(_msgSender(), uint40(block.timestamp)); VTVLVesting.sol:436: emit ClaimRevoked(_recipient, amountRemaining, uint40(block.timestamp), _claim);
https://github.com/code-423n4/2022-04-dualityfocus-findings/issues/33
token/VariableSupplyERC20Token.sol:45: _mint(account, amount); token/FullPremintERC20Token.sol:12: _mint(_msgSender(), supply_);
Use _safeMint() instead of _mint().
#0 - 0xean
2022-09-25T21:36:40Z
any report that talks about safeMint with ERC20 tokens is getting negative points.
🌟 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.3188 USDC - $9.32
++i costs less gas as compared to i++ for unsigned integer, as per-increment is cheaper(its about 5 gas per iteration cheaper)
i++ increments i and returns initial value of i. Which means
uint i = 1; i++; // ==1 but i ==2
But ++i returns the actual incremented value:
uint i = 1; ++i; // ==2 and i ==2 , no need for temporary variable here
In the first case, the compiler has create a temporary variable (when used) for returning 1 instead of 2.
VTVLVesting.sol:353: for (uint256 i = 0; i < length; i++) {
Change post-increment to pre-increment.
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 PER LOOP
VTVLVesting.sol:353: for (uint256 i = 0; i < length; i++) {
0 is less efficient than != 0 for unsigned integers (with proof) != 0 costs less gas compared to > 0 for unsigned integers in require statements with the optimizer enabled (6 gas) Proof: While it may seem that > 0 is cheaper than !=, this is only true without the optimizer enabled and outside a require statement. If you enable the optimizer at 10k AND you’re in a require statement, this will save gas. You can see this tweet for more proof: https://twitter.com/gzeon/status/1485428085885640706
VTVLVesting.sol:107: require(_claim.startTimestamp > 0, "NO_ACTIVE_CLAIM"); VTVLVesting.sol:256: require(_linearVestAmount + _cliffAmount > 0, "INVALID_VESTED_AMOUNT"); VTVLVesting.sol:257: require(_startTimestamp > 0, "INVALID_START_TIMESTAMP"); VTVLVesting.sol:263: require(_releaseIntervalSecs > 0, "INVALID_RELEASE_INTERVAL"); VTVLVesting.sol:449: require(bal > 0, "INSUFFICIENT_BALANCE"); token/VariableSupplyERC20Token.sol:27: require(initialSupply_ > 0 || maxSupply_ > 0, "INVALID_AMOUNT"); token/FullPremintERC20Token.sol:11: require(supply_ > 0, "NO_ZERO_MINT");
https://twitter.com/gzeon/status/1485428085885640706
I suggest changing > 0 with != 0. Also, please enable the Optimizer.
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 &&
Breakdown each condition in a separate require statement (though require statements should be replaced with custom errors)
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");
I suggest replacing revert strings with custom errors.
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.
https://github.com/code-423n4/2021-12-yetifinance-findings/issues/274
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");
Strict inequalities (>) are more expensive than non-strict ones (>=). This is due to some supplementary checks (ISZERO, 3 gas. I suggest using >= instead of > to avoid some opcodes here:
VTVLVesting.sol:187: return (vestAmt > _claim.amountWithdrawn) ? vestAmt : _claim.amountWithdrawn;
https://code4rena.com/reports/2022-04-badger-citadel/#g-31--is-cheaper-than
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;
https://github.com/code-423n4/2022-05-backd-findings/issues/108
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 number;
instead of uint number = 0;
VTVLVesting.sol:27: uint112 public numTokensReservedForVesting = 0; VTVLVesting.sol:148: uint112 vestAmt = 0;
I suggest removing explicit initializations for default values.
// Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled.
Refer Here Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past
AccessProtected.sol:12: mapping(address => bool) private _admins; // user address => admin? mapping VTVLVesting.sol:45: bool isActive; // whether this claim is active (or revoked)
https://code4rena.com/reports/2022-06-notional-coop#8-using-bools-for-storage-incurs-overhead