SwapX

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/SwapX.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity = 0.8.28;

import "./Storage.sol";
import "./contracts-upgradeable/access/OwnableUpgradeable.sol";
import "./contracts-upgradeable/interfaces/IERC1271Upgradeable.sol";
import "./contracts-upgradeable/proxy/utils/Initializable.sol";
import "./contracts-upgradeable/security/PausableUpgradeable.sol";
import "./contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "./contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "./contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import {FlapshLib} from "./extLib/FlapshLib.sol";
import {FourMemeLib} from "./extLib/FourMemeLib.sol";

import {UniV2Lib} from "./extLib/UniV2Lib.sol";
import {UniV3Lib} from "./extLib/UniV3Lib.sol";
import {UniV4Lib} from "./extLib/UniV4Lib.sol";

import {Common} from "./extLib/common.sol";
import "./interfaces/IFlapTokenManager.sol";
import "./interfaces/IFourTokenManager.sol";
import "./interfaces/IPancakeV3SwapCallback.sol";
import "./interfaces/IUniswapV2Pair.sol";
import "./interfaces/IUniswapV3Pool.sol";
import "./interfaces/IUniswapV3SwapCallback.sol";
import "./interfaces/IWETH.sol";
import "./libs/CallbackValidation.sol";
import "./libs/Path.sol";
import "./libs/SafeCast.sol";
import "./libs/SafeMath.sol";
import "./libs/TickMath.sol";
import "./libs/UniswapV2Library.sol";

contract SwapX is
    Storage,
    IUniswapV3SwapCallback,
    IPancakeV3SwapCallback,
    Initializable,
    OwnableUpgradeable,
    ReentrancyGuardUpgradeable,
    PausableUpgradeable
{
    address public pancakeFactoryV3;
    address internal constant UNISWAP_V3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
    address internal constant PANCAKE_V3_FACTORY = 0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865;
    address internal constant SUSHISWAP_V3_FACTORY = 0xbACEB8eC6b9355Dfc0269C18bac9d6E2Bdc29C4F;

    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeMath for uint256;
    using Path for bytes;
    using SafeCast for uint256;

    struct ExactInputSingleParams {
        address factoryAddress;
        address poolAddress;
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    struct ExactInputParams {
        address[] factoryAddresses;
        address[] poolAddresses;
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    struct ExactOutputSingleParams {
        address factoryAddress;
        address poolAddress;
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    struct ExactOutputParams {
        address[] factoryAddresses;
        address[] poolAddresses;
        bytes path;
        address tokenIn;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    struct ExactInputMixedParams {
        string[] routes;
        bytes path1;
        address factory1;
        address poolAddress1;
        bytes path2;
        address factory2;
        address poolAddress2;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    struct ExactOutputMixedParams {
        string[] routes;
        bytes path1;
        address factory1;
        bytes path2;
        address factory2;
        uint256 amountIn2; // only for v2-v3 router
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    struct SwapCallbackData {
        bytes path;
        address payer;
        address payerOrigin;
    }

    event Swap(
        address indexed payer,
        address indexed receiver,
        address indexed feeToken,
        uint256 amountIn,
        uint256 amountOut,
        Common.SwapDesc[] descs
    );

    event FeeCollected(address indexed token, address indexed payer, uint256 amount, uint256 timestamp);

    modifier checkDeadline(uint256 deadline) {
        require(block.timestamp <= deadline, "Transaction too old");
        _;
    }

    receive() external payable {}

    function initialize(
        address _factoryV3,
        address _pancakeFactoryV3,
        address _WETH,
        address _feeCollector,
        uint256 _feeRate
    ) public initializer {
        __Ownable_init(msg.sender);
        __ReentrancyGuard_init();
        __Pausable_init();

        factoryV3 = _factoryV3;
        pancakeFactoryV3 = _pancakeFactoryV3;
        feeCollector = _feeCollector;
        feeRate = _feeRate;
        feeDenominator = 10000;
        WETH = _WETH;
        amountInCached = type(uint256).max;
    }

    // take fee
    function takeFee(address tokenIn, uint256 amountIn) internal returns (uint256) {
        if (feeExcludeList[msg.sender]) {
            return 0;
        }

        uint256 fee = amountIn.mul(feeRate).div(feeDenominator);

        if (tokenIn == address(0) || tokenIn == WETH) {
            require(address(this).balance > fee, "insufficient funds");
            (bool success,) = address(feeCollector).call{value: fee}("");
            require(success, "SwapX: take fee error");
            emit FeeCollected(address(0), msg.sender, fee, block.timestamp);
        } else {
            IERC20Upgradeable(tokenIn).safeTransferFrom(msg.sender, feeCollector, fee);
            emit FeeCollected(tokenIn, msg.sender, fee, block.timestamp);
        }

        return fee;
    }

    function takeFeeTo(address tokenIn, uint256 amountIn) internal returns (uint256) {
        uint256 fee = amountIn.mul(feeRate).div(feeDenominator);
        if (fee > 0) {
            Common.pay(tokenIn, address(this), feeCollector, fee);
            emit FeeCollected(tokenIn, msg.sender, fee, block.timestamp);
        }
        return fee;
    }

    // function sellMemeToken2(FourMemeLib.SellFourMeme memory data) external nonReentrant whenNotPaused {
    //     // rounding down to 1e9
    //     data.amountIn = data.amountIn / 1e9 * 1e9;
    //     uint256 amount = FourMemeLib.sellMemeToken2(data);
    //     uint256 fee = takeFee(address(0), amount);
    //     require(amount - fee >= data.amountOutMinimum, "min return not reached");
    //     (bool success,) = address(msg.sender).call{value: amount - fee}("");
    //     require(success, "refund bnb error");
    // }

    // function buyMemeToken2(FourMemeLib.BuyFourMeme memory data) external payable nonReentrant whenNotPaused {
    //     require(msg.value >= data.amountIn, "invalid funds");
    //     uint256 fee = takeFee(address(0), data.amountIn);
    //     data.amountIn -= fee;
    //     FourMemeLib.buyMemeToken2(data);
    // }

    // // buy meme function, exact bnb amount
    // function buyMemeToken(address tokenManager, address token, address recipient, uint256 funds, uint256 minAmount)
    //     external
    //     payable
    //     nonReentrant
    //     whenNotPaused
    //     returns (uint256 amountOut)
    // {
    //     require(msg.value >= funds, "invalid funds");
    //     uint256 fee = takeFee(address(0), funds);
    //     FourMemeLib.buyMemeToken(tokenManager, token, recipient, funds - fee, minAmount);
    // }
    // // sell meme function, exact bnb amount

    // function sellMemeToken(FourMemeLib.SellFourMemeSingle memory data) external nonReentrant whenNotPaused {
    //     data.feeRate = feeRate;
    //     data.feeRecipient = feeCollector;
    //     data.payerOrigin = msg.sender;
    //     // rounding down to 1e9
    //     data.amountIn = data.amountIn / 1e9 * 1e9;
    //     FourMemeLib.sellMemeTokenSingle(data);
    // }

    // // buy meme function, exact bnb amount
    // function buyFlapToken(address manager, address token, address recipient, uint256 minAmount)
    //     external
    //     payable
    //     nonReentrant
    //     whenNotPaused
    //     returns (uint256 amountOut)
    // {
    //     uint256 funds = msg.value;
    //     uint256 fee = takeFee(address(0), funds);
    //     FlapshLib.BuyFlapshParams memory buyParams;
    //     buyParams.manager = manager;
    //     buyParams.token = token;
    //     buyParams.recipient = recipient;
    //     buyParams.funds = funds - fee;
    //     buyParams.minAmount = minAmount;

    //     FlapshLib.buyFlapsh(buyParams);
    // }

    // function sellFlapToken(address manager, address token, address recipient, uint256 amount, uint256 minAmount)
    //     external
    //     nonReentrant
    //     whenNotPaused
    // {
    //     uint256 balanceBefore = address(this).balance;

    //     FlapshLib.SellFlapshParams memory sellParams;
    //     sellParams.manager = manager;
    //     sellParams.token = token;
    //     sellParams.recipient = recipient;
    //     sellParams.funds = amount;
    //     sellParams.minAmount = minAmount;

    //     FlapshLib.sellFlapsh(sellParams);
    //     uint256 balanceAfter = address(this).balance;
    //     uint256 realAmountOut = balanceAfter - balanceBefore;
    //     uint256 fee = takeFee(address(0), realAmountOut);
    //     (bool success,) = address(msg.sender).call{value: realAmountOut - fee}("");
    //     require(success, "refund bnb error");
    // }

    function swap(Common.SwapDesc[] memory descs, address feeToken, uint256 amountIn, uint256 minReturn)
        public
        payable
        nonReentrant
        whenNotPaused
    {
        require(descs.length > 0, "ERR_INVALID_ARGUMENT: descs is empty");
        require(
            feeToken == descs[0].tokenIn || feeToken == descs[descs.length - 1].tokenOut,
            "ERR_INVALID_ARGUMENT: fee token is not the same as token in or out"
        );
        address payer = msg.sender;
        address receiver = msg.sender;

        address payerOrigin = msg.sender;
        uint256 amountOut = 0;
        uint256 balanceBefore = Common.getBalance(descs[descs.length - 1].tokenOut, msg.sender);
        // fromToken is feeToken
        if (feeToken == descs[0].tokenIn) {
            uint256 fee = takeFee(feeToken, amountIn);
            amountIn -= fee;
        } else {
            receiver = address(this);
        }
        for (uint256 i = 0; i < descs.length; i++) {
            Common.SwapDesc memory desc = descs[i];

            address middleReceiver = Common.getMiddleReceiver(descs, i, receiver);
            if (desc.swapType == Common.SwapType.V2_EXACT_IN) {
                amountOut = UniV2Lib.swap(payer, middleReceiver, payerOrigin, amountIn, desc);
            } else if (desc.swapType == Common.SwapType.V3_EXACT_IN) {
                amountOut = UniV3Lib.swap(payer, middleReceiver, payerOrigin, amountIn, desc);
            } else if (desc.swapType == Common.SwapType.V4_EXACT_IN) {
                amountOut = UniV4Lib.swap(payer, middleReceiver, payerOrigin, amountIn, desc);
            } else {
                revert("SwapX: invalid swap type");
            }
            // update amountIn for next swap
            if (i != descs.length - 1) {
                payer = address(this);
                amountIn = amountOut;
            }
        }
        if (feeToken == descs[descs.length - 1].tokenOut) {
            uint256 fee = takeFeeTo(feeToken, amountOut);
            amountOut -= fee;
            Common.pay(feeToken, address(this), msg.sender, amountOut);
        }
        uint256 balanceAfter = Common.getBalance(descs[descs.length - 1].tokenOut, msg.sender);
        require(balanceAfter - balanceBefore >= minReturn, "ERR_EXCEEDED_SLIPPAGE:minReturn not reached");
        emit Swap(msg.sender, msg.sender, feeToken, amountIn, amountOut, descs);
    }

    function unlockCallback(bytes memory data) external returns (bytes memory) {
        return UniV4Lib.unlockCallback(data);
    }
    // V2: Any swap, ExactIn single-hop - SupportingFeeOnTransferTokens

    function swapV2ExactIn(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOutMin,
        address poolAddress
    ) public payable nonReentrant whenNotPaused returns (uint256 amountOut) {
        require(poolAddress != address(0), "SwapX: invalid pool address");
        require(amountIn > 0, "SwapX: amout in is zero");

        bool nativeIn = false;
        if (tokenIn == address(0)) {
            require(msg.value >= amountIn, "SwapX: amount in and value mismatch");
            nativeIn = true;
            tokenIn = WETH;
            // refund
            uint256 amount = msg.value - amountIn;
            if (amount > 0) {
                (bool success,) = address(msg.sender).call{value: amount}("");
                require(success, "SwapX: refund ETH error");
            }
            uint256 fee = takeFee(address(0), amountIn);
            amountIn = amountIn - fee;
        }

        if (nativeIn) {
            pay(tokenIn, address(this), poolAddress, amountIn);
        } else {
            pay(tokenIn, msg.sender, poolAddress, amountIn);
        }

        bool nativeOut = false;
        if (tokenOut == address(0)) {
            nativeOut = true;
        }

        uint256 balanceBefore = nativeOut
            ? IERC20Upgradeable(WETH).balanceOf(address(this))
            : IERC20Upgradeable(tokenOut).balanceOf(msg.sender);

        IUniswapV2Pair pair = IUniswapV2Pair(poolAddress);
        address token0 = pair.token0();
        uint256 amountInput;
        uint256 amountOutput;
        {
            // scope to avoid stack too deep errors
            (uint256 reserve0, uint256 reserve1,) = pair.getReserves();
            (uint256 reserveInput, uint256 reserveOutput) =
                tokenIn == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
            amountInput = IERC20Upgradeable(tokenIn).balanceOf(address(pair)).sub(reserveInput);
            amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
        }
        (uint256 amount0Out, uint256 amount1Out) =
            tokenIn == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
        address to = nativeOut ? address(this) : msg.sender;
        pair.swap(amount0Out, amount1Out, to, new bytes(0));

        if (nativeOut) {
            amountOut = IERC20Upgradeable(WETH).balanceOf(address(this)).sub(balanceBefore);
            IWETH(WETH).withdraw(amountOut);
            uint256 fee = takeFee(address(0), amountOut);
            (bool success,) = address(msg.sender).call{value: amountOut - fee}("");
            require(success, "SwapX: send ETH out error");
        } else {
            amountOut = IERC20Upgradeable(tokenOut).balanceOf(msg.sender).sub(balanceBefore);
        }
        require(amountOut >= amountOutMin, "SwapX: insufficient output amount");
    }

    // V2: Any swap, ExactOut single-hop - * not support fee-on-transfer tokens *
    // function swapV2ExactOut(
    //     address tokenIn,
    //     address tokenOut,
    //     uint256 amountInMax,
    //     uint256 amountOut,
    //     address poolAddress
    //  ) payable public nonReentrant whenNotPaused returns (uint amountIn){

    //     require(poolAddress != address(0), "SwapX: invalid pool address");
    //     require(amountInMax > 0, "SwapX: amout in max is zero");

    //     bool nativeIn = false;
    //     if (tokenIn == address(0)) {
    //         tokenIn = WETH;
    //         nativeIn = true;
    //         require(msg.value >= amountInMax, "SwapX: amount in and value mismatch");
    //     }

    //     bool nativeOut = false;
    //     if (tokenOut == address(0))
    //         nativeOut = true;

    //     IUniswapV2Pair pair = IUniswapV2Pair(poolAddress);
    //     address token0 = pair.token0();
    //     { // scope to avoid stack too deep errors
    //         (uint256 reserve0, uint256 reserve1,) = pair.getReserves();
    //         (uint256 reserveInput, uint256 reserveOutput) = tokenIn == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    //         amountIn = UniswapV2Library.getAmountIn(amountOut, reserveInput, reserveOutput);
    //         require(amountIn <= amountInMax, "SwapX: eswapV2ExactOutxcessive input amount");

    //         uint256 fee = 0;
    //         if (!nativeOut) {
    //             fee = takeFee(tokenIn, amountIn);
    //             amountIn = amountIn - fee;
    //         }

    //         if(nativeIn) {
    //             pay(tokenIn, address(this), poolAddress, amountIn);
    //             uint256 amount = msg.value - amountIn - fee;
    //             // refund
    //             if (amount > 0) {
    //                 (bool success, ) = address(msg.sender).call{value: amount}("");
    //                 require(success, "SwapX: refund ETH error");
    //             }
    //         } else {
    //             pay(tokenIn, msg.sender, poolAddress, amountIn);
    //         }
    //     }
    //     (uint256 amount0Out, uint256 amount1Out) = tokenIn == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));
    //     address to = nativeOut ? address(this) : msg.sender;
    //     pair.swap(amount0Out, amount1Out, to, new bytes(0));

    //     if (nativeOut) {
    //         IWETH(WETH).withdraw(amountOut);
    //         uint256 fee = takeFee(address(0), amountOut);
    //         (bool success, ) = address(msg.sender).call{value: amountOut-fee}("");
    //         require(success, "SwapX: send ETH out error");
    //     }
    // }

    // **** SWAP ****
    // requires the initial amount to have already been sent to the first pair
    function _swap(uint256[] memory amounts, address[] memory path, address _to, address _factory) internal virtual {
        for (uint256 i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0,) = UniswapV2Library.sortTokens(input, output);
            uint256 amountOut = amounts[i + 1];
            (uint256 amount0Out, uint256 amount1Out) =
                input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));
            address to = i < path.length - 2 ? UniswapV2Library.pairFor(_factory, output, path[i + 2]) : _to;
            IUniswapV2Pair(UniswapV2Library.pairFor(_factory, input, output)).swap(
                amount0Out, amount1Out, to, new bytes(0)
            );
        }
    }

    // **** SWAP (supporting fee-on-transfer tokens) ****
    // requires the initial amount to have already been sent to the first pair
    function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to, address _factory)
        internal
        virtual
    {
        for (uint256 i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0,) = UniswapV2Library.sortTokens(input, output);
            IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(_factory, input, output));
            uint256 amountInput;
            uint256 amountOutput;
            {
                // scope to avoid stack too deep errors
                (uint256 reserve0, uint256 reserve1,) = pair.getReserves();
                (uint256 reserveInput, uint256 reserveOutput) =
                    input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
                amountInput = IERC20Upgradeable(input).balanceOf(address(pair)).sub(reserveInput);
                amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
            }
            (uint256 amount0Out, uint256 amount1Out) =
                input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
            address to = i < path.length - 2 ? UniswapV2Library.pairFor(_factory, output, path[i + 2]) : _to;
            pair.swap(amount0Out, amount1Out, to, new bytes(0));
        }
    }

    // V2-V2: Uniswap/Sushiswap, SupportingFeeOnTransferTokens and multi-hop
    function swapV2MultiHopExactIn(
        address tokenIn,
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address recipient,
        uint256 deadline,
        address factory
    ) public payable nonReentrant whenNotPaused checkDeadline(deadline) returns (uint256[] memory amounts) {
        require(amountIn > 0, "SwapX: amout in is zero");

        bool nativeIn = false;
        if (tokenIn == address(0)) {
            require(msg.value >= amountIn, "SwapX: amount in and value mismatch");
            nativeIn = true;
            tokenIn = WETH;
            // refund
            uint256 amount = msg.value - amountIn;
            if (amount > 0) {
                (bool success,) = address(msg.sender).call{value: amount}("");
                require(success, "SwapX: refund ETH error");
            }
        }

        bool nativeOut = false;
        address tokenOut = path[path.length - 1];
        if (tokenOut == WETH) {
            nativeOut = true;
        }

        if (!nativeOut) {
            uint256 fee = takeFee(tokenIn, amountIn);
            amountIn = amountIn - fee;
        }

        address firstPool = UniswapV2Library.pairFor(factory, path[0], path[1]);
        if (nativeIn) {
            pay(tokenIn, address(this), firstPool, amountIn);
        } else {
            pay(tokenIn, msg.sender, firstPool, amountIn);
        }
        require(tokenIn == path[0], "invalid path");

        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);

        uint256 balanceBefore = IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : recipient);
        _swapSupportingFeeOnTransferTokens(path, nativeOut ? address(this) : recipient, factory);
        uint256 amountOut =
            IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : recipient).sub(balanceBefore);
        amounts[path.length - 1] = amountOut;
        require(amountOut >= amountOutMin, "SwapX: insufficient output amount");

        if (nativeOut) {
            IWETH(WETH).withdraw(amountOut);
            uint256 fee = takeFee(address(0), amountOut);
            (bool success,) = address(recipient).call{value: amountOut - fee}("");
            require(success, "SwapX: send ETH out error");
        }
    }

    // // V2-V2: Uniswap, ExactOut multi-hop, not support fee-on-transfer token in output
    // function swapV2MultiHopExactOut(
    //     address tokenIn,
    //     uint256 amountInMax,
    //     uint256 amountOut,
    //     address[] calldata path,
    //     address recipient,
    //     uint deadline,
    //     address factory
    // ) payable public nonReentrant whenNotPaused checkDeadline(deadline) returns (uint[] memory amounts){

    //     require(amountInMax > 0, "SwapX: amount in max is zero");

    //     bool nativeIn = false;
    //     if (tokenIn == address(0)) {
    //         nativeIn = true;
    //         tokenIn = WETH;
    //         require(msg.value >= amountInMax, "SwapX: amount in and value mismatch");
    //     }

    //     bool nativeOut = false;
    //     address tokenOut = path[path.length-1];
    //     if (tokenOut == WETH)
    //         nativeOut = true;

    //     amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);

    //     uint256 fee = 0;
    //     if (!nativeOut) {
    //         fee = takeFee(tokenIn, amounts[0]);
    //         amounts[0] = amounts[0] - fee;
    //         require(amounts[0] + fee <= amountInMax, 'SwapX: excessive input amount');
    //     }

    //     address firstPool = UniswapV2Library.pairFor(factory, path[0], path[1]);
    //     if (nativeIn) {
    //         pay(tokenIn, address(this), firstPool, amounts[0]);
    //         uint amount = msg.value - amounts[0] - fee;
    //         // refund
    //         if (amount > 0) {
    //             (bool success, ) = address(msg.sender).call{value: amount}("");
    //             require(success, "SwapX: refund ETH error");
    //         }
    //     } else
    //         pay(tokenIn, msg.sender, firstPool, amounts[0]);

    //     _swap(amounts, path, nativeOut ? address(this) : recipient, factory);

    //     if (nativeOut) {
    //         IWETH(WETH).withdraw(amountOut);
    //         fee = takeFee(address(0), amountOut);
    //         (bool success, ) = address(recipient).call{value: amountOut-fee}("");
    //         require(success, "SwapX: send ETH out error");
    //     }
    // }

    /// UniswapV3SwapCallback
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external override {
        require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported
        SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData));
        (address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool();
        address factory = IUniswapV3Pool(msg.sender).factory();
        require(factory == UNISWAP_V3_FACTORY || factory == SUSHISWAP_V3_FACTORY, "SwapX: Invalid pool");
        if (factory == UNISWAP_V3_FACTORY) {
            CallbackValidation.verifyCallback(UNISWAP_V3_FACTORY, tokenIn, tokenOut, fee);
        } else {
            CallbackValidation.verifyCallback(SUSHISWAP_V3_FACTORY, tokenIn, tokenOut, fee);
        }
        (bool isExactInput, uint256 amountToPay) =
            amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta));
        if (isExactInput) {
            pay(tokenIn, data.payer, msg.sender, amountToPay);

            uint256 amount =
                tokenIn == WETH ? address(this).balance : IERC20Upgradeable(tokenIn).balanceOf(address(this));
            if (amount > 0) {
                pay(tokenIn, address(this), data.payerOrigin, amount);
            }
        }
        // } else {
        //     // either initiate the next swap or pay
        //     if (data.path.hasMultiplePools()) {
        //         data.path = data.path.skipToken();
        //         exactOutputInternal(amountToPay, msg.sender, 0, data);
        //     } else {
        //         amountInCached = amountToPay;
        //         tokenIn = tokenOut; // swap in/out because exact output swaps are reversed
        //         pay(tokenIn, data.payer, msg.sender, amountToPay);
        //     }
        // }
    }
    /// PancakeV3SwapCallback

    function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external override {
        require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported
        SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData));
        (address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool();
        CallbackValidation.verifyCallback(PANCAKE_V3_FACTORY, tokenIn, tokenOut, fee);

        (bool isExactInput, uint256 amountToPay) =
            amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta));
        if (isExactInput) {
            pay(tokenIn, data.payer, msg.sender, amountToPay);

            uint256 amount =
                tokenIn == WETH ? address(this).balance : IERC20Upgradeable(tokenIn).balanceOf(address(this));
            if (amount > 0) {
                pay(tokenIn, address(this), data.payerOrigin, amount);
            }
        }
        // } else {
        //     // either initiate the next swap or pay
        //     if (data.path.hasMultiplePools()) {
        //         data.path = data.path.skipToken();
        //         exactOutputInternal(amountToPay, msg.sender, 0, data);
        //     } else {
        //         amountInCached = amountToPay;
        //         tokenIn = tokenOut; // swap in/out because exact output swaps are reversed
        //         pay(tokenIn, data.payer, msg.sender, amountToPay);
        //     }
        // }
    }

    // V3: ExactIn single-hop
    function swapV3ExactIn(ExactInputSingleParams memory params)
        external
        payable
        nonReentrant
        whenNotPaused
        checkDeadline(params.deadline)
        returns (uint256 amountOut)
    {
        require(params.amountIn > 0, "SwapX: amount in is zero");

        if (params.tokenIn == WETH || params.tokenIn == address(0)) {
            params.tokenIn = WETH;
            require(msg.value >= params.amountIn, "SwapX: amount in and value mismatch");
            // refund
            uint256 amount = msg.value - params.amountIn;
            if (amount > 0) {
                (bool success,) = address(msg.sender).call{value: amount}("");
                require(success, "SwapX: refund ETH error");
            }
        }

        bool nativeOut = false;
        if (params.tokenOut == WETH) {
            nativeOut = true;
        }

        if (!nativeOut) {
            uint256 fee = takeFee(params.tokenIn, params.amountIn);
            params.amountIn = params.amountIn - fee;
        }

        factoryV3 = params.factoryAddress;
        amountOut = exactInputInternal(
            params.poolAddress,
            params.amountIn,
            nativeOut ? address(0) : params.recipient,
            params.sqrtPriceLimitX96,
            SwapCallbackData({
                path: abi.encodePacked(params.tokenIn, params.fee, params.tokenOut),
                payer: msg.sender,
                payerOrigin: msg.sender
            })
        );

        require(amountOut >= params.amountOutMinimum, "SwapX: insufficient out amount");

        if (nativeOut) {
            IWETH(WETH).withdraw(amountOut);
            uint256 fee = takeFee(address(0), amountOut);
            (bool success,) = address(params.recipient).call{value: amountOut - fee}("");
            require(success, "SwapX: send ETH out error");
        }
    }

    // // V3: ExactOut single-hop
    // function swapV3ExactOut (
    //     ExactOutputSingleParams memory params
    // ) payable public nonReentrant whenNotPaused checkDeadline(params.deadline) returns (uint256 amountIn) {

    //     require(params.amountInMaximum > 0, "SwapX: amount in max is zero");

    //     bool nativeIn = false;
    //     if (params.tokenIn == WETH || params.tokenIn == address(0)) {
    //         params.tokenIn = WETH;
    //         nativeIn = true;
    //         require(msg.value >= params.amountInMaximum, "SwapX: amount in max and value mismatch");
    //     }

    //     bool nativeOut = false;
    //     if (params.tokenOut == WETH || params.tokenOut == address(0)) {
    //         params.tokenOut = WETH;
    //         nativeOut = true;
    //     }

    //     uint256 fee = 0;
    //     if (!nativeOut) {
    //         fee = takeFee(params.tokenIn, amountIn);
    //         amountIn = amountIn - fee;
    //         require(amountIn + fee <= params.amountInMaximum, "SwapX: too much requested");
    //     }

    //     amountIn = exactOutputInternal(
    //         params.amountOut,
    //         nativeOut ? address(0) : params.recipient,
    //         params.sqrtPriceLimitX96,
    //         SwapCallbackData({path: abi.encodePacked(params.tokenOut, params.fee, params.tokenIn), payer: msg.sender, payerOrigin: msg.sender})
    //     );

    //     if (nativeIn) {
    //         uint amount = msg.value - amountIn - fee;
    //         // refund
    //         if (amount > 0) {
    //             (bool success, ) = address(msg.sender).call{value: amount}("");
    //             require(success, "SwapX: refund ETH error");
    //         }
    //     }

    //     if (nativeOut) {
    //         IWETH(WETH).withdraw(params.amountOut);
    //         fee = takeFee(address(0), params.amountOut);

    //         (bool success, ) = address(params.recipient).call{value: params.amountOut - fee}("");
    //         require(success, "SwapX: send ETH out error");
    //     }

    //     amountInCached = type(uint256).max;
    // }

    // V3-V3: ExactIn multi-hop
    function swapV3MultiHopExactIn(ExactInputParams memory params)
        public
        payable
        nonReentrant
        whenNotPaused
        checkDeadline(params.deadline)
        returns (uint256 amountOut)
    {
        require(params.amountIn > 0, "SwapX: amount in is zero");
        if (msg.value > 0) {
            require(msg.value >= params.amountIn, "SwapX: amount in and value mismatch");
            // refund
            uint256 amount = msg.value - params.amountIn;
            if (amount > 0) {
                (bool success,) = address(msg.sender).call{value: amount}("");
                require(success, "SwapX: refund ETH error");
            }
        }

        (address tokenIn,,) = params.path.decodeFirstPool();
        if (tokenIn == WETH || tokenIn == address(0)) {
            tokenIn = WETH;
            uint256 fee = takeFee(address(0), params.amountIn);
            params.amountIn = params.amountIn - fee;
        }

        address payer = msg.sender; // msg.sender pays for the first hop

        bool nativeOut = false;
        uint256 i = 0;
        while (true) {
            bool hasMultiplePools = params.path.hasMultiplePools();

            if (!hasMultiplePools) {
                (, address tokenOut,) = params.path.decodeFirstPool();
                if (tokenOut == WETH) {
                    nativeOut = true;
                }
            }
            // the outputs of prior swaps become the inputs to subsequent ones
            factoryV3 = params.factoryAddresses[i];
            params.amountIn = exactInputInternal(
                params.poolAddresses[i],
                params.amountIn,
                hasMultiplePools ? address(this) : (nativeOut ? address(this) : params.recipient),
                0,
                SwapCallbackData({path: params.path.getFirstPool(), payer: payer, payerOrigin: msg.sender})
            );

            // decide whether to continue or terminate
            if (hasMultiplePools) {
                payer = address(this); // at this point, the caller has paid
                params.path = params.path.skipToken();
                i++;
            } else {
                amountOut = params.amountIn;
                break;
            }
        }
        require(amountOut >= params.amountOutMinimum, "SwapX: too little received");

        if (nativeOut) {
            IWETH(WETH).withdraw(amountOut);
            uint256 fee = takeFee(address(0), amountOut);

            (bool success,) = address(params.recipient).call{value: amountOut - fee}("");
            require(success, "SwapX: send ETH out error");
        }
    }

    // V3-V3: ExactOut multi-hop
    // function swapV3MultiHopExactOut(
    //     ExactOutputParams memory params
    // ) external payable nonReentrant whenNotPaused checkDeadline(params.deadline) returns (uint256 amountIn) {

    //     require(params.amountInMaximum > 0, "SwapX: amount in max is zero");
    //     if (msg.value > 0)
    //         require(msg.value >= params.amountInMaximum, "SwapX: amount in max and value mismatch");

    //     bool nativeOut = false;
    //     (address tokenOut, , ) = params.path.decodeFirstPool();
    //     if (tokenOut == WETH)
    //         nativeOut = true;

    //     uint256 fee = 0;
    //     if (!nativeOut) {
    //         fee = takeFee(params.tokenIn, amountIn);
    //         amountIn = amountIn - fee;
    //         require(amountIn + fee <= params.amountInMaximum, 'SwapX: too much requested');
    //     }

    //     exactOutputInternal(
    //         params.amountOut,
    //         nativeOut ? address(this) : params.recipient,
    //         0,
    //         SwapCallbackData({path: params.path, payer: msg.sender, payerOrigin: msg.sender})
    //     );

    //     amountIn = amountInCached;

    //     if (params.tokenIn == WETH || params.tokenIn == address(0)) {
    //         if (msg.value > 0) {
    //             // refund
    //             uint256 amount = msg.value - amountIn - fee;
    //             if (amount > 0) {
    //                 (bool success, ) = address(msg.sender).call{value: amount}("");
    //                 require(success, "SwapX: refund ETH error");
    //             }
    //         }
    //     }

    //     if (nativeOut) {
    //         IWETH(WETH).withdraw(params.amountOut);
    //         fee = takeFee(address(0), params.amountOut);
    //         (bool success, ) = address(params.recipient).call{value: params.amountOut-fee}("");
    //         require(success, "SwapX: send ETH out error");
    //     }

    //     amountInCached = type(uint256).max;
    // }

    function isStrEqual(string memory str1, string memory str2) internal pure returns (bool) {
        return keccak256(bytes(str1)) == keccak256(bytes(str2));
    }

    // Mixed: ExactIn multi-hop, token not supporting zero address
    function swapMixedMultiHopExactIn(ExactInputMixedParams memory params)
        public
        payable
        nonReentrant
        whenNotPaused
        checkDeadline(params.deadline)
        returns (uint256 amountOut)
    {
        require(params.routes.length == 2, "SwapX: only 2 routes supported");

        require(params.amountIn > 0, "SwapX: amount in is zero");

        (address tokenIn, address tokenOut1, uint24 fee1) = params.path1.decodeFirstPool();
        bool nativeIn = false;
        if (tokenIn == WETH || tokenIn == address(0)) {
            require(msg.value >= params.amountIn, "SwapX: amount in and value mismatch");
            nativeIn = true;
            tokenIn = WETH;
            // refund
            uint256 amount = msg.value - params.amountIn;
            if (amount > 0) {
                (bool success,) = address(msg.sender).call{value: amount}("");
                require(success, "SwapX: refund ETH error");
            }
            uint256 fee = takeFee(address(0), params.amountIn);
            params.amountIn = params.amountIn - fee;
        }

        if (isStrEqual(params.routes[0], "v2") && isStrEqual(params.routes[1], "v2")) {
            // uni - sushi, or verse
            address poolAddress1 = UniswapV2Library.pairFor(params.factory1, tokenIn, tokenOut1);
            if (nativeIn) {
                pay(tokenIn, address(this), poolAddress1, params.amountIn);
            } else {
                pay(tokenIn, msg.sender, poolAddress1, params.amountIn);
            }

            address[] memory path1 = new address[](2);
            path1[0] = tokenIn;
            path1[1] = tokenOut1;

            (, address tokenOut,) = params.path2.decodeFirstPool();
            address[] memory path2 = new address[](2);
            path2[0] = tokenOut1;
            path2[1] = tokenOut;
            address poolAddress2 = UniswapV2Library.pairFor(params.factory2, tokenOut1, tokenOut);

            bool nativeOut = tokenOut == WETH;

            uint256 balanceBefore = IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient);
            _swapSupportingFeeOnTransferTokens(path1, poolAddress2, params.factory1);
            _swapSupportingFeeOnTransferTokens(path2, nativeOut ? address(this) : params.recipient, params.factory2);
            amountOut =
                IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient).sub(balanceBefore);
            if (nativeOut) {
                IWETH(WETH).withdraw(amountOut);
                uint256 fee = takeFee(address(0), amountOut);
                (bool success,) = address(params.recipient).call{value: amountOut - fee}("");
                require(success, "SwapX: send ETH out error");
            }
        } else if (isStrEqual(params.routes[0], "v2") && isStrEqual(params.routes[1], "v3")) {
            address poolAddress1 = UniswapV2Library.pairFor(params.factory1, tokenIn, tokenOut1);
            if (nativeIn) {
                pay(tokenIn, address(this), poolAddress1, params.amountIn);
            } else {
                pay(tokenIn, msg.sender, poolAddress1, params.amountIn);
            }

            address[] memory path1 = new address[](2);
            path1[0] = tokenIn;
            path1[1] = tokenOut1;

            (, address tokenOut,) = params.path2.decodeFirstPool();
            bool nativeOut = tokenOut == WETH;
            uint256 balanceBefore = IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient);
            _swapSupportingFeeOnTransferTokens(path1, address(this), params.factory1);

            factoryV3 = params.factory2;
            amountOut = exactInputInternal(
                params.poolAddress2,
                IERC20Upgradeable(tokenOut1).balanceOf(address(this)),
                nativeOut ? address(this) : params.recipient,
                0,
                SwapCallbackData({path: params.path2, payer: address(this), payerOrigin: msg.sender})
            );
            amountOut =
                IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient).sub(balanceBefore);
            if (nativeOut) {
                IWETH(WETH).withdraw(amountOut);
                uint256 fee = takeFee(address(0), amountOut);
                (bool success,) = address(params.recipient).call{value: amountOut - fee}("");
                require(success, "SwapX: send ETH out error");
            }
        } else if (isStrEqual(params.routes[0], "v3") && isStrEqual(params.routes[1], "v2")) {
            (address tokenIn2, address tokenOut,) = params.path2.decodeFirstPool();
            address pairV2Address = UniswapV2Library.pairFor(params.factory2, tokenIn2, tokenOut);

            factoryV3 = params.factory1;
            uint256 amountOut1 = exactInputInternal(
                params.poolAddress1,
                params.amountIn,
                pairV2Address,
                0,
                SwapCallbackData({
                    path: abi.encodePacked(tokenIn, fee1, tokenOut1),
                    payer: msg.sender,
                    payerOrigin: msg.sender
                })
            );

            address[] memory path2 = new address[](2);
            path2[0] = tokenIn2;
            path2[1] = tokenOut;
            uint256[] memory amounts2 = UniswapV2Library.getAmountsOut(params.factory2, amountOut1, path2);
            amountOut = amounts2[amounts2.length - 1];

            bool nativeOut = tokenOut == WETH;
            uint256 balanceBefore = IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient);
            _swapSupportingFeeOnTransferTokens(path2, nativeOut ? address(this) : params.recipient, params.factory2);
            amountOut =
                IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient).sub(balanceBefore);
            if (nativeOut) {
                IWETH(WETH).withdraw(amountOut);
                uint256 fee = takeFee(address(0), amountOut);
                (bool success,) = address(params.recipient).call{value: amountOut - fee}("");
                require(success, "SwapX: send ETH out error");
            }
        }

        require(amountOut >= params.amountOutMinimum, "SwapX: too little received");
    }

    // V3: compute pool address
    function getPool(address tokenA, address tokenB, uint24 fee) public view returns (IUniswapV3Pool) {
        return IUniswapV3Pool(PoolAddress.computeAddress(factoryV3, PoolAddress.getPoolKey(tokenA, tokenB, fee)));
    }

    /// V3: Performs a single exact input swap
    function exactInputInternal(
        address poolAddress,
        uint256 amountIn,
        address recipient,
        uint160 sqrtPriceLimitX96,
        SwapCallbackData memory data
    ) private returns (uint256 amountOut) {
        // allow swapping to the router address with address 0
        if (recipient == address(0)) recipient = address(this);

        (address tokenIn, address tokenOut,) = data.path.decodeFirstPool();

        bool zeroForOne = tokenIn < tokenOut;

        (int256 amount0, int256 amount1) = IUniswapV3Pool(poolAddress).swap(
            recipient,
            zeroForOne,
            amountIn.toInt256(),
            sqrtPriceLimitX96 == 0
                ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
                : sqrtPriceLimitX96,
            abi.encode(data)
        );

        return uint256(-(zeroForOne ? amount1 : amount0));
    }

    /// Performs a single exact output swap
    // function exactOutputInternal(
    //     uint256 amountOut,
    //     address recipient,
    //     uint160 sqrtPriceLimitX96,
    //     SwapCallbackData memory data
    // ) private returns (uint256 amountIn) {
    //     // allow swapping to the router address with address 0
    //     if (recipient == address(0)) recipient = address(this);

    //     (address tokenOut, address tokenIn, uint24 fee) = data.path.decodeFirstPool();

    //     bool zeroForOne = tokenIn < tokenOut;

    //     (int256 amount0Delta, int256 amount1Delta) =
    //         getPool(tokenIn, tokenOut, fee).swap(
    //             recipient,
    //             zeroForOne,
    //             -amountOut.toInt256(),
    //             sqrtPriceLimitX96 == 0
    //                 ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
    //                 : sqrtPriceLimitX96,
    //             abi.encode(data)
    //         );

    //     uint256 amountOutReceived;
    //     (amountIn, amountOutReceived) = zeroForOne
    //         ? (uint256(amount0Delta), uint256(-amount1Delta))
    //         : (uint256(amount1Delta), uint256(-amount0Delta));
    //     // it's technically possible to not receive the full output amount,
    //     // so if no price limit has been specified, require this possibility away
    //     if (sqrtPriceLimitX96 == 0) require(amountOutReceived == amountOut);
    // }

    function pay(address token, address payer, address recipient, uint256 value) internal {
        if (token == WETH && address(this).balance >= value) {
            // pay with WETH
            IWETH(WETH).deposit{value: value}(); // wrap only what is needed to pay
            IWETH(WETH).transfer(recipient, value);
        } else if (payer == address(this)) {
            // pay with tokens already in the contract (for the exact input multihop case)
            IERC20Upgradeable(token).safeTransfer(recipient, value);
        } else {
            // pull payment
            IERC20Upgradeable(token).safeTransferFrom(payer, recipient, value);
        }
    }

    function pause() external onlyOwner {
        require(paused() == false, "paused already");
        _pause();
    }

    function unpause() external onlyOwner {
        require(paused() == true, "unpaused already");
        _unpause();
    }

    // function setWETH(address addr) external onlyOwner {
    //     require(addr != address(0), "invalid addr");
    //     WETH = addr;
    // }

    // function setWhitelistAddr(address addr) external onlyOwner {
    //     require(addr != address(0),"invalid addr");
    //     feeExcludeList[addr] = true;
    // }

    function withdraw(address token, address receiver, uint256 amount) external onlyOwner {
        if (token == address(0)) {
            (bool s,) = payable(receiver).call{value: amount}("");
            require(s, "withdraw native failed");
        } else {
            IERC20Upgradeable(token).safeTransfer(receiver, amount);
        }
    }
}
"
    },
    "contracts/Storage.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity >= 0.8.0;

contract Storage {
    uint256 public amountInCached;
    uint256 public feeRate;
    uint256 public feeDenominator;
    address public feeCollector;
    address public WETH;

    // special for ETH chain
    address public factoryV2;

    address public factoryV3;

    mapping(address => bool) public feeExcludeList;
}
"
    },
    "contracts/contracts-upgradeable/access/OwnableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import "../proxy/utils/Initializable.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    function __Ownable_init(address initialOwner) internal onlyInitializing {
        __Ownable_init_unchained(initialOwner);
    }

    function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
"
    },
    "contracts/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 */
interface IERC1271Upgradeable {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
"
    },
    "contracts/contracts-upgradeable/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0e;

    /**
     * @dev The contract is already initialized.
     */
    error AlreadyInitialized();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;
        if (!(isTopLevelCall && initialized < 1) && !(address(this).code.length == 0 && initialized == 1)) {
            revert AlreadyInitialized();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert AlreadyInitialized();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert AlreadyInitialized();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
"
    },
    "contracts/contracts-upgradeable/security/PausableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.20;

import "../proxy/utils/Initializable.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     

Tags:
ERC20, Multisig, Mintable, Burnable, Pausable, Swap, Liquidity, Upgradeable, Multi-Signature, Factory|addr:0x7b17255c69334750700807d152b062917af1870c|verified:true|block:23381247|tx:0xf2748293796a0c71dc55b410a80eb7b4f30fe4224694752d9d8e25728eb77ffa|first_check:1758107715

Submitted on: 2025-09-17 13:15:17

Comments

Log in to comment.

No comments yet.