code-423n4 / 2021-12-maple-findings

0 stars 0 forks source link

Cache external call result in the stack can save gas #62

Open code423n4 opened 2 years ago

code423n4 commented 2 years ago

Handle

WatchPug

Vulnerability details

For the result of an external call being written into a storage variable, cache and read from the stack rather than read from the storage variable can save gas.

Instances include:

IERC20Like(collateralAsset).decimals() in DebtLocker.sol#getExpectedAmount() can be cached to avoid an extra external call.

https://github.com/maple-labs/debt-locker/blob/81f55907db7b23d27e839b9f9f73282184ed4744/contracts/DebtLocker.sol#L237-L253

function getExpectedAmount(uint256 swapAmount_) external view override whenProtocolNotPaused returns (uint256 returnAmount_) {
    address collateralAsset = IMapleLoanLike(_loan).collateralAsset();
    address fundsAsset      = IMapleLoanLike(_loan).fundsAsset();

    uint256 oracleAmount =
        swapAmount_
            * IMapleGlobalsLike(_getGlobals()).getLatestPrice(collateralAsset)  // Convert from `fromAsset` value.
            * 10 ** IERC20Like(fundsAsset).decimals()                           // Convert to `toAsset` decimal precision.
            * (10_000 - _allowedSlippage)                                       // Multiply by allowed slippage basis points
            / IMapleGlobalsLike(_getGlobals()).getLatestPrice(fundsAsset)       // Convert to `toAsset` value.
            / 10 ** IERC20Like(collateralAsset).decimals()                      // Convert from `fromAsset` decimal precision.
            / 10_000;                                                           // Divide basis points for slippage

    uint256 minRatioAmount = swapAmount_ * _minRatio / 10 ** IERC20Like(collateralAsset).decimals();

    return oracleAmount > minRatioAmount ? oracleAmount : minRatioAmount;
}

Recommendation

Change to:

function getExpectedAmount(uint256 swapAmount_) external view override whenProtocolNotPaused returns (uint256 returnAmount_) {
    address collateralAsset = IMapleLoanLike(_loan).collateralAsset();
    address fundsAsset      = IMapleLoanLike(_loan).fundsAsset();

    uint256 collateralAssetDecimals = IERC20Like(collateralAsset).decimals();

    uint256 oracleAmount =
        swapAmount_
            * IMapleGlobalsLike(_getGlobals()).getLatestPrice(collateralAsset)  // Convert from `fromAsset` value.
            * 10 ** IERC20Like(fundsAsset).decimals()                           // Convert to `toAsset` decimal precision.
            * (10_000 - _allowedSlippage)                                       // Multiply by allowed slippage basis points
            / IMapleGlobalsLike(_getGlobals()).getLatestPrice(fundsAsset)       // Convert to `toAsset` value.
            / 10 ** collateralAssetDecimals                                     // Convert from `fromAsset` decimal precision.
            / 10_000;                                                           // Divide basis points for slippage

    uint256 minRatioAmount = swapAmount_ * _minRatio / 10 ** collateralAssetDecimals;

    return oracleAmount > minRatioAmount ? oracleAmount : minRatioAmount;
}
lucas-manuel commented 2 years ago

Will add thanks

pauliax commented 2 years ago

Good suggestion.