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: 45/198
Findings: 3
Award: $136.28
🌟 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
Admin can mint more than the intended max supply
In the VariableSupplyERC20Token
, if non-zero maxSupply_
is passed in the constructor, it is intended to cap the total supply. And the state variable mintableSupply
is set to be maxSupply_
(line 28) to keep track of the remaining amount to mint. As tokens are minted the mintableSupply
would be reduced (line 43).
On the other hand, if the maxSupply_
is zero, there is no cap for the total supply and the admin can mint at will.
The problem happens when the maxSupply_
was not zero, and all of intended supply was minted. In that case the mintableSupply
would be reduced to zero. After that point, if the admin calls mint
function again, it should fail, because all intended supply was minted. However, with the current implementation, it will simply skip the check (line 40), because the mintableSupply
is now zero.
// VariableSupplyERC20Token // constructor 27 require(initialSupply_ > 0 || maxSupply_ > 0, "INVALID_AMOUNT"); 28 mintableSupply = maxSupply_; // mint 36 function mint(address account, uint256 amount) public onlyAdmin { 37 require(account != address(0), "INVALID_ADDRESS"); 38 // If we're using maxSupply, we need to make sure we respect it 39 // mintableSupply = 0 means mint at will 40 if(mintableSupply > 0) { 41 require(amount <= mintableSupply, "INVALID_AMOUNT"); 42 // We need to reduce the amount only if we're using the limit, if not just leave it be 43 mintableSupply -= amount; 44 } 45 _mint(account, amount); 46 }
None
Save the maxSupply_
and compare the (token's total supply + to be minted amount) to it. It also saves gas, as there is no more need to update the mintableSupply
additionally to the total supply, which is done anyway by the openzeppelin's ERC20.
#0 - 0xean
2022-09-23T23:53:08Z
dupe of #3
🌟 Selected for report: __141345__
Also found by: 0xDecorativePineapple, CertoraInc, IllIllI, JohnSmith, MiloTruck, djxploit, hyh, rbserver, zzzitron
116.6755 USDC - $116.68
https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L295 https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L400
Some users with claim may not be able to withdraw, if the balance of the VTVLVesting
has changed
The balanceOf
the contract VTVLVesting
should be greater or equal to the numTokensReservedForVesting
, and it is ensured in the withdrawAdmin
and createClaim
.
However, there are tokens with changing balances without transferring tokens (ref). If the balance of VTVLVesting
should be reduced below the numTokensReservedForVesting
, there will be users with valid claim, who cannot withdraw their claims.
None
It should be clearly stated what kind of tokens are allowed to be used in the VTVLVesting
.
#0 - 0xean
2022-09-24T21:11:05Z
dupe of #278
🌟 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
It is possible to renounce every admin
In the below function setAdmin
, there is no safe guard to have at least one admin. It is possible to set the only admin to be false
, therefore there is no more admin remaining. The information for admins is stored in a mapping, thus it is hard to keep track of how many admins are enabled. So there can be an accident to set the last admin to be disenabled. If it happens there can be no longer any admin for the contract.
In the case to renounce all the admin, it is advised to have a separate function to prevent from renouncing all admins unintentionally.
// AccessProtected // setAdmin 39 function setAdmin(address admin, bool isEnabled) public onlyAdmin { 40 require(admin != address(0), "INVALID_ADDRESS"); 41 _admins[admin] = isEnabled; 42 emit AdminAccessSet(admin, isEnabled); 43 }
None
For setAdmin
function, ensure there is at least one admin available. For example, do not allow the msg.sender to disenable itself.
If it is intended to be able to renounce all the admins, add a separate function for that.
#0 - 0xean
2022-09-23T23:33:59Z
dupe of #469
#1 - 0xean
2022-10-09T23:13:00Z
downgrading to QA