Platform: Code4rena
Start Date: 28/09/2023
Pot Size: $36,500 USDC
Total HM: 5
Participants: 115
Period: 6 days
Judge: 0xDjango
Total Solo HM: 1
Id: 290
League: ETH
Rank: 85/115
Findings: 1
Award: $4.37
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Bauchibred
Also found by: 0x3b, 0xDetermination, 0xMosh, 0xScourgedev, 0xTheC0der, 0xTiwa, 0xWaitress, 0xdice91, 0xfusion, 0xpiken, 0xprinc, 0xweb3boy, ArmedGoose, Aymen0909, Breeje, Brenzee, Daniel526, DavidGiladi, DeFiHackLabs, Flora, Fulum, HChang26, Hama, IceBear, J4X, Krace, KrisApostolov, Maroutis, Mirror, MohammedRizwan, Norah, PwnStars, SPYBOY, TangYuanShen, Testerbot, ThreeSigma, Tricko, al88nsk, alexweb3, ast3ros, berlin-101, bin2chen, blutorque, btk, d3e4, deth, e0d1n, ether_sky, ge6a, gkrastenov, glcanvas, hals, imare, inzinko, jkoppel, jnforja, joaovwfreire, josephdara, kutugu, lotux, lsaudit, mahdirostami, merlin, n1punp, nadin, neumo, nisedo, nobody2018, oakcobalt, orion, peanuts, pep7siup, pina, ptsanev, rokinot, rvierdiiev, said, santipu_, sashik_eth, seerether, squeaky_cactus, terrancrypt, tonisives, twicek, vagrant, xAriextz, y4y
4.3669 USDC - $4.37
2 impacts are present here.
xvsUpdated()
for that user to destroy his revocable tokens.issue()
does not check whether the user has staked for 90 days nor have 1000 XVS tokens, and this will affect the total count of totalRevocable
.I believe that the first impact is more likely because if the users are required to stake for 90 days with 1000 XVS, then they can simply just claim there Prime token themselves; there is no need for the protocol to issue a token to them.
In the issue()
function, the admin can either mint a irrevocable token or a revocable token for a user without any requirements.
function issue(bool isIrrevocable, address[] calldata users) external { _checkAccessAllowed("issue(bool,address[])"); if (isIrrevocable) { for (uint256 i = 0; i < users.length; ) { Token storage userToken = tokens[users[i]]; if (userToken.exists && !userToken.isIrrevocable) { _upgrade(users[i]); } else { _mint(true, users[i]); _initializeMarkets(users[i]); } unchecked { i++; } } } else { for (uint256 i = 0; i < users.length; ) { _mint(false, users[i]); _initializeMarkets(users[i]); delete stakedAt[users[i]]; unchecked { i++;
Once the token is created for a user, if the user does not have 1000 XVS staked, anyone can call xvsUpdated()
to destroy the user's newly created Prime token, which should not be the case.
function xvsUpdated(address user) external { uint256 totalStaked = _xvsBalanceOfUser(user); bool isAccountEligible = isEligible(totalStaked); if (tokens[user].exists && !isAccountEligible) { if (tokens[user].isIrrevocable) { _accrueInterestAndUpdateScore(user); } else { _burn(user); } } else if (!isAccountEligible && !tokens[user].exists && stakedAt[user] > 0) { stakedAt[user] = 0; } else if (stakedAt[user] == 0 && isAccountEligible && !tokens[user].exists) { stakedAt[user] = block.timestamp; } else if (tokens[user].exists && isAccountEligible) { _accrueInterestAndUpdateScore(user); } }
Manual Review
issue()
could be given as a reward for users to bypass the staking duration. In this sense, since duration is not a factor, only the staking limit should still be checked and those who have 0 staked XVS should not be receiving revocable Prime tokens. Make sure that the issue()
function checks that the user has at least 1000 XVS staked, otherwise anyone can just burn their Prime tokens.
Also, since xvsUpdated()
is called by the Vault contract, the function should be marked internal instead of external so that only the vault contract can use it.
- function xvsUpdated(address user) external { + function xvsUpdated(address user) internal {
Other
#0 - c4-pre-sort
2023-10-06T21:25:49Z
0xRobocop marked the issue as duplicate of #485
#1 - c4-judge
2023-10-31T17:56:54Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#2 - c4-judge
2023-11-03T01:59:38Z
fatherGoose1 marked the issue as grade-b