Skip to content

Latest commit

 

History

History
148 lines (121 loc) · 5.6 KB

011.md

File metadata and controls

148 lines (121 loc) · 5.6 KB

Dizzy Brunette Mule

Medium

absence of _verifyStablecoinSwap check in both swap and defiToStablecoinSwap functions may result for a token to go under the minimum supply limit

Summary

absence of _verifyStablecoinSwap check in both swap and defiToStablecoinSwap functions may result for a token to go under the minimum supply limit. This is can happen because in both functions, after performing the DeFiSwap the ss.oAmount variable is never checked. This can lead to multiple swap DoSes because of low supply. This can be fixed from the MAINTAINER_ROLE but the problem here is it can happen multiple times with practically any XYZ token.

Root Cause

Absence of _verifyStablecoinSwap check in both swap and defiToStablecoinSwap functions

Internal pre-conditions

origin token to be close to it's minimum total supply and one of the two functions to be called. This can trick the off-chain executor system because at first the StablecoinSwap params are checked

External pre-conditions

None

Attack Path

  1. The executor system call one of the functions
  2. At first the check is performed correctly and but when the DiFiSwap is performed, the ss.oAmount aren't checked once again, leading to possible partial DoS of the XYZ tokens

Impact

Possible partial DoS for all XYZ tokens

PoC

No response

Mitigation

As of right now the functions look like this:

function swap(
        address wallet,
        bool directional,
        StablecoinSwap memory ss,
        DefiSwap memory defi
    ) external payable onlyRole(SWAPPER_ROLE) whenNotPaused {
        // checks if it will fail
        if (ss.destination != address(0)) _verifyStablecoinSwap(wallet, ss);
        if (defi.walletData.length != 0) _verifyDefiSwap(wallet, defi);

        if (directional) {
            // if only defi swap
            if (ss.destination == address(0)) _defiSwap(wallet, defi);
            else {
                // if defi then stablecoin swap
                //check balance to adjust second swap
                uint256 iBalance = ERC20(ss.origin).balanceOf(wallet);
                if (defi.walletData.length != 0) _defiSwap(wallet, defi);
                uint256 fBalance = ERC20(ss.origin).balanceOf(wallet);
                //change balance to reflect change
                if (fBalance - iBalance != 0) ss.oAmount = fBalance - iBalance;
                _stablecoinSwap(wallet, ss);
            }
        } else {
            // if stablecoin swap
            _stablecoinSwap(wallet, ss);
            // if only stablecoin swap
            if (defi.walletData.length != 0) _defiSwap(wallet, defi);
        }
    }
...

function defiToStablecoinSwap(
        address wallet,
        StablecoinSwap memory ss,
        DefiSwap memory defi
    ) external payable onlyRole(SWAPPER_ROLE) whenNotPaused {
        // checks if defi will fail
        _verifyDefiSwap(wallet, defi);
        // checks if stablecoin swap will fail
        _verifyStablecoinSwap(wallet, ss);

        //check balance to adjust second swap
        uint256 iBalance = ERC20(ss.origin).balanceOf(wallet);
        _defiSwap(wallet, defi);
        uint256 fBalance = ERC20(ss.origin).balanceOf(wallet);
        ss.oAmount = fBalance - iBalance;
        //change balance to reflect change
        _stablecoinSwap(wallet, ss);
    }

Add the _verifyStablecoinSwap function after changing the ss.oAmount, like this:

    function swap(
        address wallet,
        bool directional,
        StablecoinSwap memory ss,
        DefiSwap memory defi
    ) external payable onlyRole(SWAPPER_ROLE) whenNotPaused {
        // checks if it will fail
        if (ss.destination != address(0)) _verifyStablecoinSwap(wallet, ss);
        if (defi.walletData.length != 0) _verifyDefiSwap(wallet, defi);

        if (directional) {
            // if only defi swap
            if (ss.destination == address(0)) _defiSwap(wallet, defi);
            else {
                // if defi then stablecoin swap
                //check balance to adjust second swap
                uint256 iBalance = ERC20(ss.origin).balanceOf(wallet);
                if (defi.walletData.length != 0) _defiSwap(wallet, defi);
                uint256 fBalance = ERC20(ss.origin).balanceOf(wallet);
                //change balance to reflect change
                if (fBalance - iBalance != 0) ss.oAmount = fBalance - iBalance;
+             _verifyStablecoinSwap(wallet, ss);
                _stablecoinSwap(wallet, ss);
            }
        } else {
            // if stablecoin swap
            _stablecoinSwap(wallet, ss);
            // if only stablecoin swap
            if (defi.walletData.length != 0) _defiSwap(wallet, defi);
        }
    }

...

  function defiToStablecoinSwap(
        address wallet,
        StablecoinSwap memory ss,
        DefiSwap memory defi
    ) external payable onlyRole(SWAPPER_ROLE) whenNotPaused {
        // checks if defi will fail
        _verifyDefiSwap(wallet, defi);
        // checks if stablecoin swap will fail
        _verifyStablecoinSwap(wallet, ss);

        //check balance to adjust second swap
        uint256 iBalance = ERC20(ss.origin).balanceOf(wallet);
        _defiSwap(wallet, defi);
        uint256 fBalance = ERC20(ss.origin).balanceOf(wallet);
        ss.oAmount = fBalance - iBalance;
+      _verifyStablecoinSwap(wallet, ss);
        //change balance to reflect change
        _stablecoinSwap(wallet, ss);
    }