Skip to content

Jumpy Pistachio Butterfly - Wrong units in outflow USD limiter withdraw path can cause bypass of outflow cap #1468

@sherlock-admin3

Description

@sherlock-admin3

Jumpy Pistachio Butterfly

Medium

Wrong units in outflow USD limiter withdraw path can cause bypass of outflow cap

Summary

Passing mToken units to the USD outflow limiter will cause a cap bypass for the protocol as a user withdraws, because the limiter expects underlying units but is checked before converting mTokens via redeem.

Root Cause

In mErc20Host.performExtensionCall(uint256 actionType, uint256 amount, uint32 dstChainId) the code calls:
https://github.com/sherlock-audit/2025-07-malda/blob/1bd22b27698052c24299dfab68a735810562e302/malda-lending/src/mToken/host/mErc20Host.sol#L290-L293
CommonLib.checkHostToExtension(amount, ...)

then _checkOutflow(amount (here amount is mTokens for withdraw)

then for actionType == 1 performs uint256 _amount = _redeem(msg.sender, amount, false); which returns underlying.

if (actionType == 1) {
            _amount = _redeem(msg.sender, amount, false);
            emit mErc20Host_WithdrawOnExtensionChain(msg.sender, dstChainId, _amount);
        }

The issue is that the _checkOutflow check was performed on the mToken amount, not underlying.

Internal Pre-conditions

None (other than an exchangeRate > 1e18)

External Pre-conditions

None

Attack Path

USD cap window = $100. Exchange rate = 2e18 (1 mToken = 2 underlying).

User calls performExtensionCall(1 /withdraw/, 60 /mTokens/, dst)

_checkOutflow(60) passes (60 < 100).

_redeem(..., 60, ...) returns 120 underlying.

The protocol ships $120 cross-chain in a window capped at $100.

Impact

The protocol-wide USD outflow cap can be exceeded approximately by the exchange rate, enabling larger-than-intended drains to extension chains. This increases the risk of liquidity shortfalls and cross-chain imbalances.

PoC

N/A

Mitigation

Move the check after redeem and pass underlying:

if (actionType == 1) {
    uint256 underlyingOut = _redeem(msg.sender, amount /* mTokens */, false);
    _checkOutflow(underlyingOut);
    acc[dstChainId].outPerChain[msg.sender] += underlyingOut;
}

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