Infinity NFT Marketplace contest - wagmi's results

The world's most advanced NFT marketplace.

General Information

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

Infinity NFT Marketplace

Findings Distribution

Researcher Performance

Rank: 26/99

Findings: 2

Award: $325.90

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: Ruhum

Also found by: 0xDjango, GimelSec, GreyArt, auditor0517, dipp, p4st13r4, wagmi

Labels

bug
duplicate
3 (High Risk)
sponsor confirmed

Awards

276.9248 USDC - $276.92

External Links

Lines of code

https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/staking/InfinityStaker.sol#L303

Vulnerability details

Impact

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.

Proof of Concept

  1. Alice staked 1000 tokens for 12 months.
  2. After 12 months, she staked 2000 tokens for 3 months.
  3. After 1 month, now she should be able to unstake 1000 tokens because it has been staked for 13 months. But when she calls 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)); });

Tools Used

Manual Review

Use staked amount instead of vested amount in _updateUserStakedAmounts() function.

#0 - nneverlander

2022-06-22T12:58:13Z

Duplicate

#2 - HardlyDifficult

2022-07-10T14:56:55Z

Lines of code

https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/staking/InfinityStaker.sol#L210

Vulnerability details

Impact

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()

Proof of Concept

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

Tools Used

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

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter