MorpheusAI

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

We can make `Distribution::_stake` function much more gas efficient.

[l-02] We can make Distribution::_stake function much more gas efficient.

Description: The Distribution::_stake function is checking the pool.minimalStake after transferring token from user to protocol. we can check exactly pool.minimalStake before the transferring operation and the logic will work same as intended.

Impact: The Distribution::_stake function is not gas efficient.

Proof of Concept:

The below code from Distribution::_stake is showing that the function is checking userData.deposited + amount_ greater than pool.minimalStake after the transfer operations from user to contract address. We can check the same logic before doing the token transfer and the function logic would be same as it should be, see recommended mitigation.

function _stake(address user_, uint256 poolId_, uint256 amount_, uint256 currentPoolRate_) private {
require(amount_ > 0, "DS: nothing to stake");
Pool storage pool = pools[poolId_];
PoolData storage poolData = poolsData[poolId_];
UserData storage userData = usersData[user_][poolId_];
if (pool.isPublic) {
// https://docs.lido.fi/guides/lido-tokens-integration-guide/#steth-internals-share-mechanics
uint256 balanceBefore_ = IERC20(depositToken).balanceOf(address(this)); //getting this contract token balance
//calling external contract addresss
IERC20(depositToken).safeTransferFrom(_msgSender(), address(this), amount_); //transfer token from user to this contract
uint256 balanceAfter_ = IERC20(depositToken).balanceOf(address(this)); //getting this contract balance after staking
amount_ = balanceAfter_ - balanceBefore_;
//@audit it should check before any transfer of token, which will costs less gas.
@> require(userData.deposited + amount_ >= pool.minimalStake, "DS: amount too low");
totalDepositedInPublicPools += amount_;
}
userData.pendingRewards = _getCurrentUserReward(currentPoolRate_, userData);
// Update pool data
poolData.lastUpdate = uint128(block.timestamp);
poolData.rate = currentPoolRate_;
poolData.totalDeposited += amount_;
// Update user data
userData.lastStake = uint128(block.timestamp);
userData.rate = currentPoolRate_;
userData.deposited += amount_;
emit UserStaked(poolId_, user_, amount_);
}

Recommended Mitigation: The check for userData.deposited + amount_ greater than pool.minimalStake should be made before the transfer of token.

function _stake(address user_, uint256 poolId_, uint256 amount_, uint256 currentPoolRate_) private {
require(amount_ > 0, "DS: nothing to stake");
Pool storage pool = pools[poolId_];
PoolData storage poolData = poolsData[poolId_];
UserData storage userData = usersData[user_][poolId_];
+ require(userData.deposited + amount_ >= pool.minimalStake, "DS: amount too low");
if (pool.isPublic) {
// https://docs.lido.fi/guides/lido-tokens-integration-guide/#steth-internals-share-mechanics
uint256 balanceBefore_ = IERC20(depositToken).balanceOf(address(this)); //getting this contract token balance
//calling external contract addresss
IERC20(depositToken).safeTransferFrom(_msgSender(), address(this), amount_); //transfer token from user to this contract
uint256 balanceAfter_ = IERC20(depositToken).balanceOf(address(this)); //getting this contract balance after staking
amount_ = balanceAfter_ - balanceBefore_;
//@audit it should check before any transfer of token, which will costs less gas.
- require(userData.deposited + amount_ >= pool.minimalStake, "DS: amount too low");
totalDepositedInPublicPools += amount_;
}
userData.pendingRewards = _getCurrentUserReward(currentPoolRate_, userData);
// Update pool data
poolData.lastUpdate = uint128(block.timestamp);
poolData.rate = currentPoolRate_;
poolData.totalDeposited += amount_;
// Update user data
userData.lastStake = uint128(block.timestamp);
userData.rate = currentPoolRate_;
userData.deposited += amount_;
emit UserStaked(poolId_, user_, amount_);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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