Skip to content

Latest commit

 

History

History
117 lines (89 loc) · 4.38 KB

004.md

File metadata and controls

117 lines (89 loc) · 4.38 KB

Gorgeous Maroon Stallion

Medium

_verifyStablecoinSwap incorrect checks causes valid actions to fail

Summary

stablecoinSwap allows for users to swap the same token (ie ss.origin == ss.target) between 2 different addresses. Hence, since it is allowed and is a valid function for people to send tokens to a different address through a official channel: the StablecoinHandler.sol.

However, checks in _verifyStablecoinSwap are incorrect and will cause the swap to fail in certain senarios/

Root Cause

https://github.com/sherlock-audit/2024-11-telcoin/blob/b9c751b59e78a7123a636e31ecafc9147046f190/telcoin-audit/contracts/stablecoin/StablecoinHandler.sol#L194-L224

In _verifyStablecoinSwap:

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 <
-->         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 >
-->         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");
    }
}

We can see from the lines marked with an arrow that it ensures totalSupply - oAmount >= getMinLimit and totalSupply + tAmount <= getMaxLimit.

However, during a swap where origin token == target token (same token, but swapping from one address to a different address) then that is the not the inequality that should be compared.

Namely, if origin token == target token then:

  • Stablecoin(ss.origin).totalSupply() - ss.oAmount + ss.tAmount is the equation that should be compared with getMinLimit and getMaxLimit

Internal pre-conditions

Swaps done for the same token from one address to an another different address and have totalSupply near either getMinDeposits or getMaxDeposits will be affected.

External pre-conditions

No response

Attack Path

No response

Impact

No response

PoC

No response

Mitigation

Account for the senario where ss.origin == ss.target and compare the right amount with getMinLimit and getMaxLimit

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 <
+          Stablecoin(ss.origin).totalSupply() - ss.oAmount + (ss.origin == ss.target ? ss.tAmount : 0) <
           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 >
+           Stablecoin(ss.origin).totalSupply() + ss.tAmount - (ss.origin == ss.target ? ss.oAmount : 0) >
            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");
    }
}