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: 29/198
Findings: 3
Award: $269.78
🌟 Selected for report: 0
🚀 Solo Findings: 0
https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L429 https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L364
Whenever an admin revokes a claim and the recipient has any unclaimed but vested balance , the unclaimed part is also revoked. Take for instance a total amount of 365 tokens vested over 1 year with a release interval of 1 day. If the recipient at day 30 withdraws their due balance they will receive 30 tokens. If then at day 40 the admin revokes the claim the 10 unclaimed tokens are also revoked and the recipient cannot withdraw them anymore where under normal vesting rules that balance is rightfully theirs.
In the function revokeClaim()
the amount to reclaim by the admin is calculated as the finalVestAmt - _claim.amountWithdrawn
.
This should be finalVestAmt - vestedAmount(_recipient, uint40(block.timestamp)
.
Also note that this means a user should be able to withdraw the remaining part after the claim has been revoked which currently is impossible due to the hasActiveClaim() modifier on the withdraw()
function which will cause a revert because the claim's isActive
is set to false
.
NA
Replace
function revokeClaim(address _recipient) external onlyAdmin hasActiveClaim(_recipient) { .... uint112 amountRemaining = finalVestAmt - _claim.amountWithdrawn; ....
by
function revokeClaim(address _recipient) external onlyAdmin hasActiveClaim(_recipient) { ... uint112 amountRemaining = finalVestAmt - vestedAmount(_recipient, uint40(block.timestamp); ...
and change the withdraw()
function to allow a recipient to withdraw the remaining amount due.
#0 - indijanc
2022-09-24T17:17:43Z
Duplicate of #475
#1 - 0xean
2022-09-24T18:27:24Z
closing as duplicate.
🌟 Selected for report: rajatbeladiya
Also found by: 0x4non, CertoraInc, Chom, JLevick, JohnSmith, KIntern_NA, Ruhum, RustyRabbit, ak1, berndartmueller, imare, joestakey, obront, rbserver, rotcivegaf, supernova
32.8268 USDC - $32.83
https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L253 https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L129 https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L418 https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L364
The hasNoClaim()
modifier on the _createClaimUnchecked
reverts if the recipient has ever had a claim which has been canceled for whatever reason or the claim has finished (ie. endTimestamp has passed and the balance has been withdrawn).
This means a recipient can only once be given a claim and not reissued one after expiration or revocation. This might not be a huge problem for an EOA (although it also needs to be funded with ETH), but for SC wallets like Gnosis Safe it is as a new Safe would need to be created.
The hasNoClaim()
modifier reverts here when the claim's startTimestamp
is not 0.
The revokeClaim()
function does not remove the claim from the claims
mapping or set the startTimeStamp
to 0 and neither does the withdraw()
function.
NA
Incorporate the removal of finished claims from the claims mapping in the withdraw()
function (only on the last withdrawal) and revokeClaim()
function (only if there is no remaining balance)
#0 - 0xean
2022-09-24T19:07:53Z
dupe of #140
🌟 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
cliffReleaseTimestamp
as part of the Claim
struct is unnecessary.When a claim
is created the cliffReleaseTimestamp
is required to be <= startTimestamp
.
As the claim isn't valid until after the startTimestamp
this means they are in fact equal except for the case where cliffReleaseTimestamp
is 0
which is used to indicate there is no cliff release, but this can also be done with the cliffAmount
.
As a result the cliffReleaseTimestamp
can be removed from the struct and the code simplified.
Unless of course a cliff release after the start timestamp is a valid use case (in which case this is should be a higher risk rating), but I presume this is not the case as the comment explicitly affirms this.