Skip to content

Festive Linen Unicorn - Some Usual tokens are stuck in UsualSP.sol #145

@sherlock-admin2

Description

@sherlock-admin2

Festive Linen Unicorn

Medium

Some Usual tokens are stuck in UsualSP.sol

Summary

In current implementation, if UsualSP.startRewardDistribution is called before UsualSP.allocate, there will some rewardToken stuck in the UsualSP.sol contract.

Root Cause

I'll use the on-chain tx to explain the issue
After UsualSP is deployed in tx https://etherscan.io/tx/0x52fbf41f8c8f8f935b448ad8c2c11b86df3ad21727756c9230a5e27ce55afccf, UsualSP.startRewardDistribution is the first function been called in tx https://etherscan.io/tx/0x26ce95f3b7a9fd0ceb0e32ffd7c6e7a83278edb9fd1096cc4c2408f4fc463d9e, and then UsualSP.allocate is called in tx https://etherscan.io/tx/0xbcff5d36ffbd70819daf79693d7183aa6dda2aa423100b05fa82905f7188651b, and then UsualSP.startRewardDistribution is called again in https://etherscan.io/tx/0x117679127b297c0afbe033d7fb564d1b1961edac3f5dd80f986d1befca830178, and then UsualSP.stakeUsualS is called in tx https://etherscan.io/tx/0x7cdb5ce4ba96f446292c97d93ba61eb0aeef86a9fa9c6ceda779ec4fc2328303.

  1. while the first UsualSP.startRewardDistribution is called, _rewardPerToken is called to calculate rewardPerToken, because of totalStaked is 0, function _rewardPerToken will return 0 in RewardAccrualBase.sol#L111.
106     function _rewardPerToken() internal view virtual returns (uint256 rewardPerToken) {
110         if (totalStaked() == 0) {
111             return $.rewardPerTokenStored; <<<--- function will do early return here
112         } else {
...
126         }
127     }
  1. until UsualSP.stakeUsualS is called , $.rewardPerTokenStored will keep zero because totalStaked will always returns 0
68     function totalStaked() public view override returns (uint256) {
469         UsualSPStorageV0 storage $ = _usualSPStorageV0();
470         return $.usualS.balanceOf(address(this));
471     }
  1. In such case, until the third UsualSP.startRewardDistribution tx, the $.rewardPerToken will be zero, so the Usual token distributed by the first two UsualSP.startRewardDistribution will be now redistributed.

Internal Pre-conditions

None

External Pre-conditions

None

Attack Path

After UsualSP is deployed in tx https://etherscan.io/tx/0x52fbf41f8c8f8f935b448ad8c2c11b86df3ad21727756c9230a5e27ce55afccf, UsualSP.startRewardDistribution is the first function been called in tx https://etherscan.io/tx/0x26ce95f3b7a9fd0ceb0e32ffd7c6e7a83278edb9fd1096cc4c2408f4fc463d9e, and then UsualSP.allocate is called in tx https://etherscan.io/tx/0xbcff5d36ffbd70819daf79693d7183aa6dda2aa423100b05fa82905f7188651b, and then UsualSP.startRewardDistribution is called again in https://etherscan.io/tx/0x117679127b297c0afbe033d7fb564d1b1961edac3f5dd80f986d1befca830178, and then UsualSP.stakeUsualS is called in tx https://etherscan.io/tx/0x7cdb5ce4ba96f446292c97d93ba61eb0aeef86a9fa9c6ceda779ec4fc2328303.

Impact

According to https://app.blocksec.com/explorer/tx/eth/0x26ce95f3b7a9fd0ceb0e32ffd7c6e7a83278edb9fd1096cc4c2408f4fc463d9e and https://app.blocksec.com/explorer/tx/eth/0x117679127b297c0afbe033d7fb564d1b1961edac3f5dd80f986d1befca830178, there will be (603890260786742293065600 + 640072478128131577209600) usual been transferred to UsualSP contract, and according to https://coinmarketcap.com/currencies/usual/, usual's highest price is $1.64, which means there will be (603890260786742293065600 + 640072478128131577209600) *1.64 / 1e18 = 2040098.8918203933 $ stuck UsualSP contract

PoC

As the third UsualSP.startRewardDistribution is called in https://etherscan.io/tx/0x3ce4db9b3f00fb63d334ef68f463a5342b65152782778da6abd6446550b2af78 in block 21281033, if we query $.rewardPerTokenStored(whose storage location is 0xece341a81e9ef81761e1d1d3338155bec39de2969454f2b1605e36884716e506)

  1. before the third startRewardDistribution
>>cast storage -r https://rpc.ankr.com/eth  0xa55AF35E5F4bb6A82E0A290570BcE38Ce2757d37 0xece341a81e9ef81761e1d1d3338155bec39de2969454f2b1605e36884716e506 -b 21281032

0x0000000000000000000000000000000000000000000000000000000000000000
  1. after the thirs startRewardDistribution
>>cast storage -r https://rpc.ankr.com/eth  0xa55AF35E5F4bb6A82E0A290570BcE38Ce2757d37 0xece341a81e9ef81761e1d1d3338155bec39de2969454f2b1605e36884716e506 -b 21281034
0x000000000000000000000000000000000000000000000060626ab0b0803049e0

No response

Mitigation

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions