MorpheusAI

MorpheusAI
Foundry
22,500 USDC
View results
Submission Details
Severity: medium
Invalid

Stake doesn't validate if the staker is payable or not, which raise issue on claiming reward

Summary

Stake doesn't validate if the staker is payable or not, which raise issue on claiming reward

Vulnerability Details

When staking, protocol doesn't check if staker address is payable or not,

File: Distribution.sol
150: function stake(uint256 poolId_, uint256 amount_) external poolExists(poolId_) poolPublic(poolId_) {
151: _stake(_msgSender(), poolId_, amount_, _getCurrentPoolRate(poolId_));
152: }

here, the staker address is captured using _msgSender() which is from OZ lib, OwnableUpgradeable -> ContextUpgradeable

function _msgSender() internal view virtual returns (address) {
return msg.sender;
}

previously, this _msgSender() is payable, but since OZ move Context from GSN to utils directory, the payable is removed.

So, address which is not payable can still stake. (there is no payable requirement in _stake function). Generally if staker address is an EOA, it is payable, but if the staker is a contract, which is might not be a payable type (lack of receive ether), there will be an issue on claiming.

When claiming, it will call L1Sender with msg.value,

File: Distribution.sol
154: function claim(uint256 poolId_, address user_) external payable poolExists(poolId_) {
...
173: // Transfer rewards
174: L1Sender(l1Sender).sendMintMessage{value: msg.value}(user_, pendingRewards_, _msgSender());
177: }

the sendMintMessage send msg.value with 3 parameters, with the implementation as follow:

File: L1Sender.sol
124: function sendMintMessage(address user_, uint256 amount_, address refundTo_) external payable onlyDistribution {
...
130: ILayerZeroEndpoint(config.gateway).send{value: msg.value}(
131: config.receiverChainId, // communicator LayerZero chainId
132: receiverAndSenderAddresses_, // send to this address to the communicator
133: payload_, // bytes payload
134: payable(refundTo_), // refund address
135: address(0x0), // future parameter
136: bytes("") // adapterParams (see "Advanced Features")
137: );
138: }

here the refundTo is set to _msgSender(), while it is expected to be a payable on sendMintMessage. Thus, a non payable address can stake, but will be issue when claim.

Impact

A non payable address (contract) can stake, but then can't claim, loosing their reward.

Tools Used

Manual analysis

Recommendations

Consider explicitly prevent a non-payable address to stake, by casting the _msgSender() with payable, or else they will can't claim their reward.

Updates

Lead Judging Commences

inallhonesty Lead Judge
over 1 year ago
inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.