Skip to content

Latest commit

 

History

History
111 lines (86 loc) · 4.77 KB

042.md

File metadata and controls

111 lines (86 loc) · 4.77 KB

Rich Chrome Rattlesnake

Medium

Transaction will revert even when stablecoin total supply is within min and max range.

Summary

A transaction will revert even when the total supply of stablecoin remains within its minimum and maximum limit due to a logic issue in the StablecoinHandler._verifyStablecoinSwap() function.

Root Cause

The issue arises in the StablecoinHandler._verifyStablecoinSwap() function, where it checks if the total supply of the ss.origin falls below the minimum limit or if the ss.target exceeds the maximum limit. Here's the relevant portion of the code:

    function _verifyStablecoinSwap(
        address wallet,
        StablecoinSwap memory ss
    ) internal view nonZero(ss) {
        // Ensure the wallet address is not zero.
        if (wallet == address(0)) revert ZeroValueInput("WALLET");

        // For the origin currency:
        if (isXYZ(ss.origin)) {
            // Ensure the total supply does not drop below the minimum limit after burning the specified amount.
            if (
205:            Stablecoin(ss.origin).totalSupply() - ss.oAmount <
                getMinLimit(ss.origin)
            ) revert InvalidMintBurnBoundry(ss.origin, ss.oAmount);
        } else if (ss.liquiditySafe == address(0)) {
            // Ensure the liquidity safe is provided for ERC20 origin tokens.
            revert ZeroValueInput("LIQUIDITY SAFE");
        }

        // For the target currency:
        if (isXYZ(ss.target)) {
            // Ensure the total supply does not exceed the maximum limit after minting the specified amount.
            if (
217:            Stablecoin(ss.target).totalSupply() + ss.tAmount >
                getMaxLimit(ss.target)
            ) revert InvalidMintBurnBoundry(ss.target, ss.tAmount);
        } else if (ss.liquiditySafe == address(0)) {
            // Ensure the liquidity safe is provided for ERC20 target tokens.
            revert ZeroValueInput("LIQUIDITY SAFE");
        }
    }

Let us consider the case that a user merely send tokens to someone without converting tokens. Then ss.origin and ss.target is the same, ss.oAmount and ss.tAmount is the same, but the ss.destination will be different with wallet. In such a case, no change of the total supply of ss.origin is expected but the function may revert at L205 or L217.

Internal pre-conditions

  • ss.origin == ss.target

External pre-conditions

No response

Attack Path

  1. Assume the min limit of eUSD is 200, the max limit is 400 and the total supply is 300.
  2. A user attempts to send 200 eUSD to another user, and the swapper role calls StablecoinHandler.verifyStablecoinSwap() function with ss.origin = ss.target = eUSD and ss.oAmount = ss.tAmount = 200.
  3. The function reverts at L205 because 200 - 200 < 300 is true, even though no actual supply change occurs.

Impact

Transactions will be reverted unnecessarily even when the total supply remains within acceptable limits, preventing users from sending XYZ tokens.

PoC

No response

Mitigation

Modify the StablecoinHandler._verifyStablecoinSwap() function as follows.

    function _verifyStablecoinSwap(
        address wallet,
        StablecoinSwap memory ss
    ) internal view nonZero(ss) {
        // Ensure the wallet address is not zero.
        if (wallet == address(0)) revert ZeroValueInput("WALLET");

        // For the origin currency:
        if (isXYZ(ss.origin)) {
            // Ensure the total supply does not drop below the minimum limit after burning the specified amount.
            if (
--              Stablecoin(ss.origin).totalSupply() - ss.oAmount <
++              ss.origin != ss.target && Stablecoin(ss.origin).totalSupply() - ss.oAmount <
                getMinLimit(ss.origin)
            ) revert InvalidMintBurnBoundry(ss.origin, ss.oAmount);
        } else if (ss.liquiditySafe == address(0)) {
            // Ensure the liquidity safe is provided for ERC20 origin tokens.
            revert ZeroValueInput("LIQUIDITY SAFE");
        }

        // For the target currency:
        if (isXYZ(ss.target)) {
            // Ensure the total supply does not exceed the maximum limit after minting the specified amount.
            if (
--              Stablecoin(ss.target).totalSupply() + ss.tAmount >
++              ss.origin != ss.target && Stablecoin(ss.target).totalSupply() + ss.tAmount >
                getMaxLimit(ss.target)
            ) revert InvalidMintBurnBoundry(ss.target, ss.tAmount);
        } else if (ss.liquiditySafe == address(0)) {
            // Ensure the liquidity safe is provided for ERC20 target tokens.
            revert ZeroValueInput("LIQUIDITY SAFE");
        }
    }