Skip to content

Instantly share code, notes, and snippets.

@GalloDaSballo
Last active April 20, 2024 10:13
Show Gist options
  • Select an option

  • Save GalloDaSballo/1810dbd7e4a339647fc66d190c1d1e51 to your computer and use it in GitHub Desktop.

Select an option

Save GalloDaSballo/1810dbd7e4a339647fc66d190c1d1e51 to your computer and use it in GitHub Desktop.

Invariant - Solvency after fee

We need to verify that everyone can cash out their tokens I feel due to double fee some tokens may be unclaimable

Analysis

Overall the swap facility needs a lot of tests

Some ideas:

  • Can withdraw 100% of tokens
  • Fee amounts make sense

High - swapExactIn uses the wrong Math

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L96-L103

        // Process fee
        uint256 fee = _processFee(amountIn18Decimals, _feeRate);
        uint256 amountOut = amountIn18Decimals - fee;

        emit SwapExactIn(msg.sender, amountIn, amountOut, fee, to, deadline);
        mintedToken.mint(to, amountOut);
        inputToken.transferFrom(msg.sender, address(vault), amountIn);
        inputToken.transfer(feeRecipient, fee);

You should take the fee in amountIn and not in amountOut

So you should do the following:

  • fee = _processFee(amountIn, _feeRate);
  • amountIn = amountIn - fee
  • amountOut = amountIn * to18ConversionFactor

The current setup will reduce the wrong amount from minted, the fee will be too small and you'll overmint BTCN

High - Incorrect Fee Burn logic on swapExactOut

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L127-L137

        uint256 amountOut18Decimals = amountOut * to18ConversionFactor;
        uint256 fee = _processFee(amountOut, _feeRate);

        uint256 amountInWithFee = amountOut18Decimals + fee;

        require(amountInWithFee <= amountInMax, "Amount in with fee exceeds max");

        emit SwapExactOut(msg.sender, amountOut, amountInWithFee, fee, to, deadline);
        mintedToken.burnFrom(msg.sender, amountInWithFee);
        inputToken.transferFrom(address(vault), to, amountOut);
        mintedToken.transfer(feeRecipient, fee);

Should be:

        uint256 amountOut18Decimals = amountOut * to18ConversionFactor;
        uint256 fee = _processFee(amountOut18Decimals, _feeRate); /// @audit Fee should be in 18 decimals since it' in minted Token!

        uint256 amountInWithFee = amountOut18Decimals + fee;

        require(amountInWithFee <= amountInMax, "Amount in with fee exceeds max");

        emit SwapExactOut(msg.sender, amountOut, amountInWithFee, fee, to, deadline);
        mintedToken.transferFrom(msg.sender, amountInWithFee); /// @audit Transfer 100%
        mintedToken.burn(amountOut18Decimals); /// @audit Burn non-fee
        mintedToken.transfer(feeRecipient, fee); /// @audit Fee is sent

        inputToken.transferFrom(address(vault), to, amountOut); /// @audit User receives the amountOut
        

High - Lack of Burn From

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L135-L136

        mintedToken.burnFrom(msg.sender, amountInWithFee);

Bitcorn doesn't have burnFrom https://github.com/cornbase/bitcorn-token/blob/main/src/Bitcorn.sol

QA / MED - Fee Rate is not in BPS, it's in 1e18

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L56-L57

        feeRate = _feeRate.safeCastTo96();

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L43-L44

    /// @param _feeRate The inital fee rate for swaps, in basis points.

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L146-L150

    /// @notice Sets the fee rate for swaps.
    /// @param _feeRate The new fee rate, as a percentage in basis points.
    function setFeeRate(uint256 _feeRate) external requiresAuth {
        feeRate = _feeRate.safeCastTo96();
    }

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L176-L178

    function _processFee(uint256 _amountIn, uint256 _feeRate) internal returns (uint256) {
        return _amountIn * _feeRate / 1e18;
    }

QA / MED - Since you could change fee mid txs, use amountOutMin

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L84-L85

    function swapExactIn(uint256 amountIn, uint256 amountOutMin, address to, uint256 deadline)

amountOutMin is unused, best to use it since it's in the interface

And if fees change mid tx the user could lose a bit

A bit of a scammy finding so either fix or ack

QA - Good old safeTransfer -> Make it OOS if you're only using wBTC / BTCN

QA

Make constant

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L35-L36

    uint256 MIN_AMOUNT_IN = 1e15; // Mitigate rounding issues through minimum size

Consider making all caps for immutables

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L23-L29

    IERC20 immutable inputToken;
    IERC20Mintable immutable mintedToken;

    IVault public immutable vault;
    address public immutable feeRecipient;
    uint256 public immutable to18ConversionFactor;

Best to fetch these from the Silo

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L66-L67

        to18ConversionFactor = 10 ** uint256(18 - inputToken.decimals());

Informational

Nitpick - Shouldn't this be "whenSwapInEnabled"

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L69-L70

    modifier onlySwapInEnabled() {

< means "before which"

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L83-L84

    /// @param deadline The timestamp by which the swap must be completed.

Rename to "getFee" or "computeFee"

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L176

_processFee

Gas

Only used once, no need to cache

https://github.com/cornbase/swap-facility/blob/c121fe09396e0665dbdaff7b325f6c8c16933d9a/src/SwapFacility.sol#L93-L94

        uint256 _feeRate = uint256(feeRate);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment