Platform: Code4rena
Start Date: 14/06/2022
Pot Size: $50,000 USDC
Total HM: 19
Participants: 99
Period: 5 days
Judge: HardlyDifficult
Total Solo HM: 4
Id: 136
League: ETH
Rank: 26/99
Findings: 2
Award: $325.90
🌟 Selected for report: 0
🚀 Solo Findings: 0
276.9248 USDC - $276.92
Function _updateUserStakedAmounts()
of InfinityStaker
is use to update amount and timestamp of user’s staked funds when user call unstake()
.
But instead of using staked amount to check, it uses vested amount to check (amount > vestedThreeMonths
) and set amount to 0, which means user’s staked funds is lost completely.
unstake(1000)
, she also loses her 2000 tokens staked for 3 months.For more detail, please check this test. You can add it to staker.js
to run
it.only('lose funds when unstake', async function () { // approve erc20 await approveERC20(signer1.address, token.address, amountStaked2, signer1, infinityStaker.address); // stake 1000 token for 12 months await infinityStaker.connect(signer1).stake(toBN(1000), 3); // increase time by 1 year await network.provider.send('evm_increaseTime', [12 * MONTH + 1 * DAY]); await network.provider.send('evm_mine', []); // stake 2000 token for 3 months await infinityStaker.connect(signer1).stake(toBN(2000), 1); // check current staked fund expect(await infinityStaker.getUserTotalStaked(signer1.address)).to.equal(toBN(3000)); // unstake 1000 token await infinityStaker.connect(signer1).unstake(toBN(1000)); // check current staked fund expect(await infinityStaker.getUserTotalStaked(signer1.address)).to.equal(toBN(2000)); });
Manual Review
Use staked amount instead of vested amount in _updateUserStakedAmounts()
function.
#0 - nneverlander
2022-06-22T12:58:13Z
Duplicate
#1 - nneverlander
2022-07-05T11:29:23Z
#2 - HardlyDifficult
2022-07-10T14:56:55Z
🌟 Selected for report: joestakey
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 0xf15ers, 0xkowloon, 0xmint, 8olidity, BowTiedWardens, Chom, Cityscape, Czar102, ElKu, FSchmoede, Funen, GimelSec, GreyArt, IllIllI, KIntern, Kaiziron, Kenshin, Lambda, MadWookie, MiloTruck, PPrieditis, Picodes, Ruhum, Sm4rty, StErMi, TerrierLover, TomJ, Treasure-Seeker, VAD37, WatchPug, Wayne, _Adam, a12jmx, abhinavmir, antonttc, apostle0x01, asutorufos, berndartmueller, cccz, cloudjunky, codexploder, cryptphi, csanuragjain, defsec, delfin454000, fatherOfBlocks, georgypetrov, hake, hansfriese, horsefacts, hyh, k, kenta, nxrblsrpr, oyc_109, peritoflores, rajatbeladiya, reassor, rfa, robee, sach1r0, saian, samruna, shenwilly, simon135, sorrynotsorry, sseefried, throttle, unforgiven, wagmi, zzzitron
48.9776 USDC - $48.98
Function getUserStakeLevel
in InfinityStaker
should return level of user given staked amount and staked threshold. But this function assumes that stake thresholds is ascending and will have unexpected behaviours if it’s not.
This can happen when owner mistakenly set stake thresholds updateStakeLevelThreshold()
For example, if stake thresholds is [1000, 12000, 10000, 20000]
with level [BRONZE, SILVER, GOLD, PLATINUM]
respectively. Alice's stake power is 11000. She should have GOLD
level but becauce it checks low level first then she will only receive SILVER
level
Manual review
Should add check to make sure stake thresholds is always ascending.
Or in getUserStakeLevel
can check high level first (e.g check PLATINUM -> GOLD -> … -> BRONZE)
#0 - HardlyDifficult
2022-07-12T07:56:20Z
Agree that it would be good to validate the inputs are sorted correctly here. Lowering risk and converting into a QA report for the warden