Platform: Code4rena
Start Date: 09/02/2024
Pot Size: $60,500 USDC
Total HM: 17
Participants: 283
Period: 12 days
Judge:
Id: 328
League: ETH
Rank: 240/283
Findings: 1
Award: $1.01
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: klau5
Also found by: 0xAlix2, 0xCiphky, 0xDetermination, 0xG0P1, 0xMosh, 0xabhay, 14si2o_Flint, AlexCzm, Aymen0909, CodeWasp, DanielArmstrong, FloatingPragma, Giorgio, JCN, Jorgect, Kalogerone, KmanOfficial, Kow, KupiaSec, McToady, SpicyMeatball, VAD37, WoolCentaur, ZanyBonzy, alexxander, alexzoid, almurhasan, blutorque, csanuragjain, denzi_, dipp, djxploit, evmboi32, handsomegiraffe, haxatron, immeas, jesjupyter, ke1caM, klau5, lanrebayode77, lil_eth, merlin, merlinboii, nuthan2x, peanuts, shaflow2, shaka, sl1, solmaxis69, stakog, swizz, t0x1c, tallo, ubermensch, vnavascues, yotov721
1.0089 USDC - $1.01
In this scenario, it's crucial to acknowledge that the user's rewards will be forfeited due to their inability to reclaim the stakeAtRisk NRN. This unfortunate outcome stems from the transaction reverting, rendering the reclaim process impossible. As a result, the user faces the loss of their earned rewards, adding to the significance of addressing the underlying issue to ensure a fair and functional system.
In the given scenario, Bob, as the owner of a fighter NFT with ID 0, initially stakes 3000 * 10 ** 18 NRN (Neuron tokens) and engages in a battle, unfortunately losing all his NRN in the first round. Subsequently, Bob decides to unstake by calling the Unstake
function to transfer his fighter NFT. Upon transferring the NFT to Alice, the amountLost
associated with Bob's address reflects a value of 3000 * 10 ** 18 NRN.
Now, Alice participates in a battle and emerges victorious. Despite Alice not staking any NRN herself, the stakeAtRisk for the token ID 0 remains at 3000 * 10 ** 18 NRN, allowing her to claim the NRN. However, when the game server executes the updateBattleRecord
function, triggering the _addResultPoints
function, which subsequently invokes reclaimNRN
in StakeAtRisk.sol
, a transaction revert occurs. This reversal transpires because the amountLost
for Alice is decremented to 0, resulting in an arithmetic underflow.
Test :
function testReclaim() public{ address bob = address(4); address alice = address(5); uint256 stakeAmount = 3_000 * 10 ** 18; _fundUserWith4kNeuronByTreasury(bob); _fundUserWith4kNeuronByTreasury(alice); _mintFromMergingPool(bob); vm.startPrank(bob); _rankedBattleContract.stakeNRN(stakeAmount,0); vm.stopPrank(); vm.startPrank(address(_GAME_SERVER_ADDRESS)); _rankedBattleContract.updateBattleRecord(0, 50, 2, 1500, true); vm.stopPrank(); vm.startPrank(bob); _rankedBattleContract.unstakeNRN(5000 * 10 ** 18,0); _fighterFarmContract.safeTransferFrom(bob,alice,0); vm.stopPrank(); address nn = _fighterFarmContract.ownerOf(0); console.log(alice,nn); uint stakeRisk = _stakeAtRiskContract.getStakeAtRisk(0); console.log(stakeRisk); uint amountL = _stakeAtRiskContract.amountLost(alice); console.log(amountL); vm.startPrank(address(_GAME_SERVER_ADDRESS)); _rankedBattleContract.updateBattleRecord(0, 50, 0, 1500, true); vm.stopPrank(); uint stakeRisk1 = _stakeAtRiskContract.getStakeAtRisk(0); console.log(stakeRisk1); }
Results :
Running 7 tests for test/StakeAtRisk.t.sol:StakeAtRiskTest [PASS] testGetStakeAtRisk() (gas: 879916) [FAIL. Reason: panic: arithmetic underflow or overflow (0x11)] testReclaim() (gas: 1049485) [PASS] testReclaimNRN() (gas: 811810) [PASS] testSetNewRoundFromNonRankedBattle() (gas: 13479) [PASS] testSetNewRoundWithNoStakeAtRisk() (gas: 52248) [PASS] testSetNewRoundWithStakeAtRisk() (gas: 889159) [PASS] testUpdateAtRiskRecords() (gas: 885255) Test result: FAILED. 6 passed; 1 failed; 0 skipped; finished in 14.07ms
Manual Review
function unstakeNRN(uint256 amount, uint256 tokenId) external { require(_fighterFarmInstance.ownerOf(tokenId) == msg.sender, "Caller does not own fighter"); require(stakeAtRisk[roundId][tokenId] == 0, "Cant unstake") //Add the Following check if (amount > amountStaked[tokenId]) { amount = amountStaked[tokenId]; } amountStaked[tokenId] -= amount; globalStakedAmount -= amount; stakingFactor[tokenId] = _getStakingFactor( tokenId, _stakeAtRiskInstance.getStakeAtRisk(tokenId) ); _calculatedStakingFactor[tokenId][roundId] = true; hasUnstaked[tokenId][roundId] = true; bool success = _neuronInstance.transfer(msg.sender, amount); if (success) { if (amountStaked[tokenId] == 0) { _fighterFarmInstance.updateFighterStaking(tokenId, false); } emit Unstaked(msg.sender, amount); } }
DoS
#0 - c4-pre-sort
2024-02-24T04:52:26Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-02-24T04:52:34Z
raymondfam marked the issue as duplicate of #1641
#2 - c4-judge
2024-03-12T03:34:04Z
HickupHH3 changed the severity to 2 (Med Risk)
#3 - c4-judge
2024-03-12T04:01:25Z
HickupHH3 changed the severity to 3 (High Risk)
#4 - c4-judge
2024-03-12T04:03:30Z
HickupHH3 changed the severity to 2 (Med Risk)
#5 - c4-judge
2024-03-13T10:05:38Z
HickupHH3 marked the issue as satisfactory