MorpheusAI

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

GatewayRouter.outboundTransfer() does not check the call value, this may lead to amount of overplus loss or get stuck

Summary

If there is a difference between totalDepositedInPublicPools and depositTokenContractBalance_ ( calculated in Distribution.overplus() ) then the difference will be bridged from L1 Ethereum to L2 Arbitrum by calling the bridgeOverplus(). Then there are 3 variables that are input for this function:

uint256 gasLimit_, // maxgas : max gas for L2 retryable execution
uint256 maxFeePerGas_, // gasPriceBid : gas price for L2 retryable ticket
uint256 maxSubmissionCost_ // base submission cost L2 retryable tick3et

These three variables determine how many call values must be sent to carry out execution on the destination chain ( L2 Arbitrum ). Based on docs and tests from Arbitrum, call value must pass maxSubmissionCost + maxGas * gasPriceBid.

The main problem here, Distribution.bridgeOverplus() and L1Sender.sendDepositToken() do not check whether the value of call value == maxSubmissionCost + maxGas * gasPriceBid. The amount of overplus can be loss if the L1 call value provided is insufficient to cover maxSubmissionCost, or stuck if insufficient to cover maxSubmissionCost + maxGas * gasPriceBid.

Vulnerability Details

Scenario :

  1. The protocol check if there are any overplus in Distribution.sol contract using overplus()

  2. If there are any overplus, protocol call Distribution.bridgeOverplus() . In this function, any amount overplus send to L1Sender.sol

  3. Then Distribution call L1Sender.sendDepositToken(). In this function, overplus ( stETH ) wrapped to wstETH . After that the overplus send to L2 Arbitrum using GatewayRouter.outboundTransfer()

  4. Because the call value for GatewayRouter.outboundTransfer() not checked, there are two fatal possibilities that could occur if the value is less than the value it should be :

  • if call value < maxSubmissionCost the retryable ticket creation will fail and fund is lost

  • if call value < maxSubmissionCost + maxGas * gasPriceBid the amount of overplus will stuck because the ticket would require manual execution

Impact

The amount of overplus loss or get stuck

Tools Used

Manual Review

Recommended Mitigation

Consider add the call value check on L1Sender.sendDepositToken()

File : contracts/L1Sender.sol
function sendDepositToken(
uint256 gasLimit_,
uint256 maxFeePerGas_,
uint256 maxSubmissionCost_
) external payable onlyDistribution returns (bytes memory) {
uint256 expectedEth = maxSubmissionCost_ + (gasLimit_ * maxFeePerGas_);
require(_maxSubmissionCost > 0, "NO_SUBMISSION_COST");
require(msg.value == expectedEth, "WRONG_ETH_VALUE");
DepositTokenConfig storage config = depositTokenConfig;
// Get current stETH balance
uint256 amountUnwrappedToken_ = IERC20(unwrappedDepositToken).balanceOf(address(this));
// Wrap all stETH to wstETH
uint256 amount_ = IWStETH(config.token).wrap(amountUnwrappedToken_);
bytes memory data_ = abi.encode(maxSubmissionCost_, "");
return
IGatewayRouter(config.gateway).outboundTransfer{value: msg.value}(
config.token,
config.receiver,
amount_,
gasLimit_,
maxFeePerGas_,
data_
);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue
0xdemon Submitter
over 1 year ago
inallhonesty Lead Judge
over 1 year ago
inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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