Lucene search

K
code423n4Code4renaCODE423N4:2023-03-NEOTOKYO-FINDINGS-ISSUES-335
HistoryMar 15, 2023 - 12:00 a.m.

Unclaimed rewards will be stuck unrecoverable

2023-03-1500:00:00
Code4rena
github.com
4
unclaimed rewards
security vulnerability
staking rewards
stuck rewards
pool rewards
calculation issue
rewards time updates

Lines of code
<https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1409&gt;
<https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1264&gt;
<https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1388-L1392&gt;

Vulnerability details

Impact

The vulnerability relies in: <https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1388-L1392&gt;

                         unchecked {
			uint256 share = points * _PRECISION / pool.totalPoints * totalReward;
			uint256 daoShare = share * pool.daoTax / (100 * _DIVISOR);
			share /= _PRECISION;
			daoShare /= _PRECISION;
			return ((share - daoShare), daoShare);
		}

The problem is that if the pool rewards are very high or/and you have staked you asset for a long time, the shares and daoShare will return 0, disabling you from claiming the staking rewards and it updates the rewards time, therefore losing the rewards: <https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1432-L1435&gt;

	lastRewardTime[_recipient][AssetType.S1_CITIZEN] = block.timestamp;
	lastRewardTime[_recipient][AssetType.S2_CITIZEN] = block.timestamp;
	lastRewardTime[_recipient][AssetType.LP] = block.timestamp;

So, if pools have lots of assets and you have been staking for a long time, you will lose all your staking reward. It is impossible to guess the exact amount because the only value that we know is _PRECISION, which is 1e12.

Proof of Concept

Steps to reproduce the attack:

  • Stake as much NFTs in the pool

  • Leave on of the NFTs for let’s say 100 days. You can do that with Foundry or HARDHAT

  • Let’s say that citizen1 is 100 points and there are 5_000 citizens 1 staked.
    Calculation: share: 100 * 1e12 / (5_000 * 100) * 1e18 –> the following will return 0, and also daoshare = 0.

  • Reward times will be updated

              lastRewardTime[_recipient][AssetType.S1_CITIZEN] = block.timestamp;
    

    lastRewardTime[_recipient][AssetType.S2_CITIZEN] = block.timestamp;
    lastRewardTime[_recipient][AssetType.LP] = block.timestamp;

Tools Used

Manual

Recommended Mitigation Steps

Make a different calculation of the rewards of each pool or increase the _PRECISION param so that it does not return 0


The text was updated successfully, but these errors were encountered:

All reactions