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: 10/198
Findings: 3
Award: $636.42
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Trust
Also found by: 0xSky, CertoraInc, KIntern_NA, bin2chen, hansfriese, neko_nyaa, neumo, rokinot, wastewa
In VTVLVesting._baseVestedAmount()
, the funds might be locked inside the contract forever with uint112
overflow.
Currently, it doesn't consider uint112
overflow during multiply and it's very likely to happen when the vesting duration is not short like 1 year.
In this case, the funds for that claim will be locked inside the contract permanently.
As we can see here, the allowed token amount for uint112
is around 5e15
after ignoring 18 decimals.
But when the vest duration is 1 year here, truncatedCurrentVestingDurationSecs
is 3600 * 24 * 365 = 31536000
.
So if token amount is greater than 5e15 / 31536000 = 1.6e8
, it will revert with uint112
overflow and below scenario would be possible.
A
. linearVestAmount = 2e8
, VestDuration(= endTimestamp - startTimestamp) = 2 years
.A
tried to withdraw the available amount but it's failed here.It will be easier to happen when the token decimals are longer than 18.
Solidity Visual Developer of VSCode
We should modify this calculation like below.
uint112 linearVestAmount = uint112(((uint256)_claim.linearVestAmount) * truncatedCurrentVestingDurationSecs / finalVestingDurationSecs);
#0 - 0xean
2022-09-24T19:19:15Z
dupe of #95
🌟 Selected for report: Czar102
Also found by: 0xSmartContract, Lambda, Respx, csanuragjain, hansfriese, sashik_eth
The reentrance is possible with VTVL.withdrawAdmin()
.
As we can see here, the contract should contain enough balance for users' active claims so that users can trust they get receive the shares if the claims aren't revoked by admin.
But the admin can withdraw all funds using VTVL.withdrawAdmin()
and users can't receive as expected even though the claims are active.
As we can see here, the token for this contract can be any ERC20 token and we can consider it's possible to be an ERC777 token.
Also, we can assume the token contains a hook _beforeTokenTransfer()
like this.
numTokensReservedForVesting = 90
.withdrawAdmin()
using _amountRequested = 10
._beforeTokenTransfer()
and he calls withdrawAdmin()
again with the same param.tokenAddress.balanceOf(address(this))
will be still 100 because the balance is not updated yet and it will work properly.revokeClaim()
, there is an opportunity that users can withdraw the available tokens by front-running but they wouldn't take care of withdrawAdmin()
as it's designed to withdraw some free tokens.Even though the admin can create/revoke claims as he wants, the above case should be fixed.
Solidity Visual Developer of VSCode
Recommend using the ReentrancyGuard library of Openzeppelin
#0 - 0xean
2022-09-25T13:56:54Z
dupe of #6
🌟 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
AccessProtected.setAdmin()
can enable/disable admin access and msg.sender(active admin)
can disable himself using this function.
So if admin disables himself by fault, admin roles will be lost forever.
I don't think the admin might disable himself when there is only one admin.
But below scenario would be possible.
A
and B
now and they discussed to remove one of them.A
removed his role by calling setAdmin(A, false)
.B
forgot which admin to remove and removed his account also at the same time.Solidity Visual Developer of VSCode
I think it would be good to add more requirements for this function.
We can modify msg.sender
can't disable himself like below.
function setAdmin(address admin, bool isEnabled) public onlyAdmin { require(admin != address(0), "INVALID_ADDRESS"); if(!isEnabled) { require(admin != msg.sender, "can't disable"); } _admins[admin] = isEnabled; emit AdminAccessSet(admin, isEnabled); }
#0 - 0xean
2022-09-23T23:25:53Z
dupe of #469
#1 - 0xean
2022-10-09T23:49:47Z
downgraded to QA