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: 79/198
Findings: 3
Award: $28.69
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Czar102
Also found by: 0xDecorativePineapple, 0xNazgul, 0xSky, 0xbepresent, 0xmatt, Atarpara, Bahurum, DimitarDimitrov, Franfran, GimelSec, JGcarv, JLevick, Junnon, OptimismSec, Rolezn, Ruhum, Soosh, Tomo, Trust, __141345__, adriro, ajtra, bin2chen, cRat1st0s, cccz, cryptonue, d3e4, innertia, jag, joestakey, neumo, obront, pashov, pauliax, pcarranzav, peanuts, rajatbeladiya, rbserver, reassor, seyni, wagmi, zzykxx, zzzitron
0.7375 USDC - $0.74
If maxSupply was assigned different to 0, the mint function should limit the token creation. Comment in the code says:
@param maxSupply_ - What's the maximum supply. The contract won't allow minting over this amount. Set to 0 for no limit.
The problem is that even when the maxSupply was assigned different to 0, the restriction was ignored in the mint function because the function will reduce the mintableSupply until 0 and zero also means "no limit".
An unlimited supply will make the token inflationary, behavior that is not correct if the creator setting up the max supply
I made a basic test in Brownie/Python. It helps to figure out the problem. https://gist.github.com/0xbepresent/6b54369211019a3f90d431ccff599fdf
VisualStudio/Python
Consider a standard implementation https://docs.openzeppelin.com/contracts/2.x/erc20-supply#fixed-supply
#0 - 0xean
2022-09-24T00:38:27Z
dupe of #3
🌟 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
https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61/contracts/AccessProtected.sol#L39
If a wrong/invalid address is given when calling setAdmin, some contract's functions could be lost access. While setAdmin checks for zero address, there is no validation of the new address being correct.
Also, there is no validation that the contract has at least one admin.
The design of the setAdmin function can block the contract's functions forever, so functions like "createClaim", "createClaimsBatch", "withdrawAdmin", "revokeClaim", "withdrawOtherToken", "setAdmin", "mint" would be locked.
contracts/AccessProtected.sol#L39 function setAdmin(address admin, bool isEnabled) public onlyAdmin {
setAdmin(invalid_address, True)
setAdmin(alice_address, False)
VisualStudio Code
Change the single-step admin assignation to a two-step process where the current Admin first approves a new address as a pendingAdmin. That pendingAdmin has to then claim the ownership in a separate transaction. An incorrectly set pendingAdmin can be reset by changing it again to the correct one who can then successfully claim it in the second step.
Also, in the admin assignation process make sure that the contract has at least one admin.
#0 - 0xean
2022-09-23T23:27:24Z
2 step process should be QA, but will mark as a duplicate of #469 re: the check to ensure the final admin doesn't remove themselves.
#1 - 0xean
2022-10-09T22:59:10Z
downgrading to QA per #496.
🌟 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.086 USDC - $9.09
Insatance of this issue:
contracts/VTVLVesting.sol#L398 function withdrawAdmin(uint112 _amountRequested) public onlyAdmin {
Import of unnecessary files costs deployment gas
contracts/VTVLVesting.sol#L6 import "@openzeppelin/contracts/access/Ownable.sol"; contracts/AccessProtected.sol#L5 import "@openzeppelin/contracts/access/Ownable.sol";
Require statements including conditions with the && operator can be broken down in multiple require statements to save gas.
contracts/VTVLVesting.sol#L344 require(_startTimestamps.length == length && _endTimestamps.length == length && _cliffReleaseTimestamps.length == length && _releaseIntervalsSecs.length == length && _linearVestAmounts.length == length && _cliffAmounts.length == length, "ARRAY_LENGTH_MISMATCH" );
There is no risk that the loop counter can overflow, using solidity's unchecked block saves gas.
Instance of this issue:
contracts/VTVLVesting.sol#L353 for (uint256 i = 0; i < length; i++) {
Unchecked implementation example:
for (uint256 i; i < 10;) { j++; unchecked { ++i; } }
x =+ y
costs more gas than x = x + y
for state variablesInstance of this issue:
contracts/VTVLVesting.sol#L302 numTokensReservedForVesting += allocatedAmount; // track the allocated amount
Implement the required statement that amount should be greater than 0 in order to save gas.
Instances of this issue:
contracts/token/VariableSupplyERC20Token.sol#L36 function mint(address account, uint256 amount) public onlyAdmin {