MyPerfectCalculatorLive

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/MyPerfectCalculatorLive.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
pragma solidity 0.8.26;\r
// import "hardhat/console.sol";\r
import { IERC20 } from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";\r
import { Address } from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol";\r
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol";\r
\r
interface IWETH is IERC20 {\r
    function deposit() external payable;\r
    function withdraw(uint256 amount) external payable;\r
}\r
import { Token } from "contracts/token/Token.sol";\r
import { TokenLibrary } from "contracts/token/TokenLibrary.sol";\r
import { IVault as IBalancerVault } from "contracts/vault/IVault.sol";\r
import { IFlashLoanRecipient as IBalancerFlashLoanRecipient } from "contracts/vault/IFlashLoanRecipient.sol";\r
import { castTokens as castToBalancerTokens } from "contracts/vault/BalancerUtils.sol";\r
import { IBancorNetwork, IFlashLoanRecipient } from "contracts/exchanges/interfaces/IBancorNetwork.sol";\r
import { IBancorNetworkV2 } from "contracts/exchanges/interfaces/IBancorNetworkV2.sol";\r
import { ICarbonController } from "contracts/exchanges/interfaces/ICarbonController.sol";\r
import { ICarbonPOL } from "contracts/exchanges/interfaces/ICarbonPOL.sol";\r
\r
import { IUniversalRouter } from "./src/IUniversalRouter.sol";\r
import { IPermit2 } from "https://github.com/Uniswap/permit2/src/interfaces/IPermit2.sol";\r
\r
interface IHyperliquid {\r
    function swapIn(\r
        bool swap0To1,\r
        uint256 amountIn,\r
        uint256 amountOutMin,\r
        address recipient\r
    ) external payable returns (uint256 amountOut);\r
}\r
\r
interface IHyperliquidPools {\r
    function getPoolTokens(address pool_) external view returns (address token0_, address token1_);\r
}\r
\r
interface IMooniSwap {\r
    function swap(IERC20 src, IERC20 dst, uint256 amount, uint256 minReturn, address referral) external payable returns(uint256 result);\r
}\r
\r
interface IUniswapV4Helper {\r
    struct SwapData {\r
        bytes commands;\r
        bytes[] inputs;\r
        address inputToken;\r
        address outputToken;\r
        uint256 value;\r
    }\r
    function buildSwapData(TradeRoute calldata route) external pure returns (SwapData memory);\r
}\r
\r
interface IGSP {    \r
    function sellQuote(address to) external payable returns (uint256);\r
    function sellBase(address to) external payable returns (uint256);\r
    function _BASE_TOKEN_() external view returns (IERC20);\r
    function _QUOTE_TOKEN_() external view returns (IERC20);\r
}\r
\r
import "contracts/executors/IExecutor.sol";\r
\r
interface IBribeVault {\r
    function sendBribe(uint256 amount) external;\r
}\r
\r
contract MyPerfectCalculatorLive {  \r
\r
    using TokenLibrary for Token;\r
    mapping(uint16 => address) public executors;\r
    mapping(uint256 => bool) private _delegatePlatforms;\r
    mapping(uint256 => address) private _platformRouterOverride;\r
    mapping(address => mapping(address => bool)) private _approved;\r
\r
    function setIUniswapV4Helper(address router) external {\r
        _IUniswapV4Helper = IUniswapV4Helper(router);\r
    }\r
\r
    function setPlatformRouter(uint256 platformId, address router) external {\r
        _platformRouterOverride[platformId] = router;\r
    }\r
\r
    function getsetExecutor(uint16 platformId, address newExecutor) public returns (address) {\r
        if (newExecutor != address(0)) {\r
            executors[platformId] = newExecutor;\r
        }\r
        return executors[platformId];\r
    }    \r
\r
    function getsetDelegatePlatform(uint256 platformId, bool update, bool enabled) public returns (bool) {\r
        if (update) {\r
            _delegatePlatforms[platformId] = enabled;\r
        }\r
        return _delegatePlatforms[platformId];\r
    }\r
\r
    using SafeERC20 for IERC20;\r
    \r
    error InvalidWethTrade();\r
    error UnsupportedPlatform();\r
    error EthTransferFailed();\r
\r
    address private owner;\r
    \r
    // WETH9 contract\r
    address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\r
    address internal constant ETH_TOKEN = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;\r
    IERC20 internal constant BNT_TOKEN = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C);\r
																						\r
    IBalancerVault internal constant _balancerVault= IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);\r
	IBancorNetwork internal constant _bancorNetworkV3 = IBancorNetwork(0xeEF417e1D5CC832e619ae18D2F140De2999dD4fB);																												\r
    IBancorNetworkV2 internal constant _bancorNetworkV2 = IBancorNetworkV2(0x2F9EC37d6CcFFf1caB21733BdaDEdE11c823cCB0);\r
    ICarbonController internal constant _iCarbonController = ICarbonController(0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1);\r
    IHyperliquidPools internal constant hyperPools = IHyperliquidPools(0xC93876C0EEd99645DD53937b25433e311881A27C);\r
    IUniswapV4Helper internal _IUniswapV4Helper;\r
\r
    IPermit2 internal constant PERMIT2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3);\r
    IUniversalRouter internal constant UNIVERSAL_ROUTER = IUniversalRouter(payable(0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af));\r
    IBribeVault internal constant bribeVault = IBribeVault(0xA19451094F53ED12Ff0277879C19cf40e51b7df0);\r
\r
    constructor(        \r
        address _ownerAddress \r
    ) {\r
        owner = _ownerAddress;\r
        _delegatePlatforms[3] = true;\r
        _delegatePlatforms[4] = true;\r
        _delegatePlatforms[15] = true;        \r
        _platformRouterOverride[15] = 0x208fF5Eb543814789321DaA1B5Eb551881D16b06;\r
        _delegatePlatforms[18] = true;\r
        _platformRouterOverride[18] = 0x62e31802c6145A2D5E842EeD8efe01fC224422fA;\r
\r
        _delegatePlatforms[12] = true;//FluidDexLite\r
        _delegatePlatforms[13] = true;//FluidDex\r
        _delegatePlatforms[9] = true;//Curve\r
        //LOKAAL _delegatePlatforms[11] = false;//GSP\r
\r
        // 1 2 66 8 11 14 16 17 zijn lokaal gezet hier => alles waar msg.value wordt gebruikt\r
        executors[15] = 0x6D7fa3d442A81E6adEFaAeED5C75CE6C190aA296;\r
        executors[3] = 0x54A9C8265aB7F03161409b36C30e55f85eccD6C1; //dit mag deze zijn zonder try catch\r
        executors[4] = 0xcCe3C009E029dd9CEc6D1C9A734091B907A8b79a; //dit mag deze zijn zonder try catch   \r
        executors[18] = 0x041b49bBbF5693EC2DB0da96ee638E30Baac32d8;\r
        _IUniswapV4Helper = IUniswapV4Helper(0xdEcBDbd2af4cbc55F3401b6bc60Af60ce6a852cD); // note the interface type here    \r
        executors[12] = 0xBD918D8f7F4c42FE40A6bC71B6Dd950dE9Dac483;//FluidDexLite\r
        executors[13] = 0x90b738ba024d61a01C4B7e43b6bfE1Df1F92c23F;//FluidDex\r
        executors[9] = 0x6c87AF08bA3fB1A8C37848d635cEdB364BF2028B;//Curve\r
        //LOKAAL executors[11] = 0x59926F11DD1D8c6798c07dCb1F01d19e4653d251;//GSP no delegate   \r
\r
        _delegatePlatforms[12] = true;//FluidDexLite\r
        _delegatePlatforms[13] = true;//FluidDex\r
        executors[12] = 0xE5c3Be8fbeBB1391C380EBC443a5Ce01cFF3D66d;//FluidDexLite\r
        executors[13] = 0x90b738ba024d61a01C4B7e43b6bfE1Df1F92c23F;//FluidDex\r
        _delegatePlatforms[9] = true;//Curve\r
        executors[9] = 0x6443d7cd185432448EE8Ac081FE04d7a389f1977;//Curve\r
       \r
    }\r
\r
    receive() external payable {}\r
    fallback() external payable {}\r
   \r
    function flashloanAndArbBalancerMulti(\r
        uint256 fl_amount,\r
        address fl_token,\r
        TradeRoute[] memory routes,\r
        uint256 gas_amount,\r
        uint256 bribe_amount,\r
        uint256 bribe_amount_eth\r
    ) public {       \r
        bytes memory encodedData = abi.encode(routes, fl_amount, gas_amount, bribe_amount, bribe_amount_eth); 								  \r
        _takeFlashloan_balancer(fl_amount, fl_token, encodedData);		 \r
    }\r
\r
    function _takeFlashloan_balancer(uint256 fl_amount, address fl_token, bytes memory data) private {\r
        \r
        IERC20[] memory tokens = new IERC20[](1);\r
        uint256[] memory amounts = new uint256[](1);\r
\r
        tokens[0] = IERC20(fl_token);\r
        amounts[0] = fl_amount;        \r
\r
        if(fl_token == address(BNT_TOKEN))\r
        {\r
            // take a flashloan on Bancor v3, execution continues in `onFlashloan`        \r
            _bancorNetworkV3.flashLoan(\r
                Token(fl_token),\r
                fl_amount,\r
                IFlashLoanRecipient(address(this)),\r
                data\r
            );\r
        }\r
        else {\r
            // take a flashloan on Balancer, execution continues in `receiveFlashLoan`\r
            _balancerVault.flashLoan(\r
                IBalancerFlashLoanRecipient(address(this)),\r
                castToBalancerTokens(tokens),\r
                amounts,\r
                data\r
            );\r
        }        \r
    }				  \r
\r
    function onFlashLoan(\r
            address /*caller*/,\r
            IERC20 token,\r
            uint256 amount,\r
            uint256 feeAmount,\r
            bytes memory data\r
        ) external { \r
\r
            _handleFlashLoan(token, amount, feeAmount, data);\r
    } \r
\r
    function receiveFlashLoan(\r
        IERC20[] memory tokens,\r
        uint256[] memory amounts,\r
        uint256[] memory feeAmounts,\r
        bytes memory userData\r
    ) external { 	\r
        _handleFlashLoan(tokens[0], amounts[0], feeAmounts[0], userData);\r
    }\r
\r
    function _handleFlashLoan(\r
        IERC20 token,\r
        uint256 amount,\r
        uint256 feeAmount,\r
        bytes memory userData\r
    ) internal {\r
\r
        (, , uint256 gas_amount, uint256 bribe_amount, uint256 bribe_amount_eth) = abi.decode(userData, (TradeRoute[], uint256, uint256, uint256, uint256));\r
\r
// uint256 balance_before = token.balanceOf(address(this));   \r
// console.log("balance_before", balance_before);\r
\r
        _arbitrageV2Internal(userData);\r
\r
// uint256 balance_after = token.balanceOf(address(this));   \r
// console.log("balance_before", balance_after);\r
\r
        SafeERC20.safeTransfer(IERC20(token), msg.sender, amount + feeAmount);        \r
\r
        uint256 balance = token.balanceOf(address(this));   \r
// console.log("balance", balance);\r
        if(balance < gas_amount){\r
            require(1 > 1 , "balance lt gas!");    \r
        }\r
        if ((bribe_amount > 0) && (bribe_amount < balance)){\r
            // SafeERC20.safeTransfer(IERC20(token), block.coinbase, bribe_amount);\r
            // balance = balance - bribe_amount;\r
            // ✅ NEW LOGIC:\r
            if (bribe_amount_eth > 0) {\r
                bribeVault.sendBribe(bribe_amount_eth);\r
            }\r
        }\r
        transferToken(address(token), balance);\r
\r
// require(1 > 1 , "einde flashloan");\r
    }    \r
\r
    function transferToken(address tokenAddress, uint256 balance) public payable{\r
        if (tokenAddress == NATIVE_TOKEN) {\r
            payable(owner).transfer(address(this).balance);\r
        } \r
\r
        if (balance > 0) {\r
            SafeERC20.safeTransfer(IERC20(tokenAddress), owner, balance);\r
        }\r
\r
    }\r
\r
    function _arbitrageV2Internal(\r
        bytes memory userData\r
    ) internal  {\r
\r
        (TradeRoute[] memory path, uint256 amount, , ) = abi.decode(userData, (TradeRoute[], uint256, uint256, uint256));\r
        \r
        uint256 ret = amount;    \r
        for (uint256 j = 0; j < path.length; j = _uncheckedInc(j)) {\r
            TradeRoute memory r = path[j]; \r
            r.sourceAmount = ret;  \r
            ret = trade(r);\r
\r
            //sometimes we buy a token but the return is different because of some internal fee\r
            // if((address(path[j].targetToken) != NATIVE_TOKEN) && (address(path[j].targetToken) != ETH_TOKEN)){\r
            //     uint256 balance = IERC20(address(path[j].targetToken)).balanceOf(address(this));                    \r
            //     if(balance < ret){                        \r
            //         ret = balance;\r
            //     }\r
            // }\r
        }\r
    }    \r
\r
    function tryMooniSwap(\r
                TradeRoute memory route\r
            ) internal returns (uint256 quote) \r
    { \r
        uint256 val = 0;\r
        address sourceToken = address(route.sourceToken); \r
        address targetToken = address(route.targetToken); \r
\r
        if(address(route.sourceToken) == ETH_TOKEN){\r
            sourceToken = address(0);    \r
        }\r
        if(address(route.targetToken) == ETH_TOKEN){\r
            targetToken = address(0);    \r
        }    \r
\r
        if (sourceToken != address(0)) {\r
            _approveAndTransfer(route.sourceToken, address(route.customAddress), route.sourceAmount, false);\r
        } else {\r
            val = route.sourceAmount;\r
        }\r
\r
        quote = IMooniSwap(route.customAddress).swap{value: val}(\r
            IERC20(sourceToken),\r
            IERC20(targetToken),\r
            route.sourceAmount,\r
            0,\r
            address(0)\r
        );\r
    }\r
\r
    function tryGSPSwap(\r
        TradeRoute memory route\r
    ) internal returns (uint256 quote) {\r
        \r
        IERC20(route.sourceToken).forceApprove(route.customAddress, type(uint256).max);\r
        IERC20(route.sourceToken).safeTransfer(route.customAddress, route.sourceAmount);\r
\r
        address source = address(IGSP(route.customAddress)._QUOTE_TOKEN_());\r
\r
        if(route.sourceToken == source){\r
            quote = IGSP(route.customAddress).sellQuote(address(this));\r
        }\r
        else{\r
            quote = IGSP(route.customAddress).sellBase(address(this));\r
        }        \r
    }\r
\r
    function tryBancorPOLSwap(\r
                TradeRoute memory route\r
            ) internal returns (uint256 quote) \r
    { \r
        uint256 val = 0;\r
        \r
        if (route.sourceToken != NATIVE_TOKEN) {\r
            _approveAndTransfer(route.sourceToken, address(route.customAddress), route.sourceAmount, false);\r
        } else {\r
            val = route.sourceAmount;\r
        }\r
\r
        uint128 targetAmount = ICarbonPOL(route.customAddress).expectedTradeReturn(Token(route.targetToken), uint128(route.sourceAmount));\r
\r
        ICarbonPOL(route.customAddress).trade{value: val}(\r
                Token(route.targetToken),\r
                targetAmount,\r
                uint128(route.sourceAmount));\r
        quote = route.targetToken == NATIVE_TOKEN \r
                    ? address(this).balance\r
                    : IERC20(route.targetToken).balanceOf(address(this));\r
        return quote;         \r
    }\r
\r
    function tryHyperSwap(\r
                TradeRoute memory route\r
            ) internal returns (uint256 quote) \r
    { \r
        uint256 val = 0;\r
        if (route.sourceToken != NATIVE_TOKEN && route.sourceToken != ETH_TOKEN) {\r
            _approveAndTransfer(route.sourceToken, address(route.customAddress), route.sourceAmount, false);\r
        } else {\r
            val = route.sourceAmount;\r
        }\r
        // Get swap direction\r
        bool swapDirection;\r
        (address token0,) = hyperPools.getPoolTokens(route.customAddress);\r
        swapDirection = route.sourceToken == token0;\r
        \r
        // Perform swap\r
        quote = IHyperliquid(route.customAddress).swapIn{value: val}(\r
            swapDirection,\r
            route.sourceAmount,\r
            0,\r
            address(this)\r
        ); \r
\r
    }\r
    function tryBancorV2Swap(\r
                TradeRoute memory route\r
            ) internal returns (uint256 quote) \r
    { \r
\r
        address[] memory path = new address[](3);\r
        path[0] = address(route.sourceToken);\r
        path[1] = route.customAddress; \r
        path[2] = address(route.targetToken);        \r
        \r
        uint256 val = _prepSwap(address(route.sourceToken), address(_bancorNetworkV2), route.sourceAmount);\r
        quote = _bancorNetworkV2.convertByPath{ value: val }(\r
                path,\r
                route.sourceAmount,\r
                route.minTargetAmount,\r
                address(0x0),\r
                address(0x0),\r
                0\r
            );\r
    }\r
\r
    function tryBancorV3Swap(\r
                TradeRoute memory route\r
            ) internal returns (uint256 quote) \r
    { \r
        uint256 val = _prepSwap(address(route.sourceToken), address(_bancorNetworkV3), route.sourceAmount);\r
        quote = _bancorNetworkV3.tradeBySourceAmount{ value: val }(\r
                Token(route.sourceToken),\r
                Token(route.targetToken),\r
                route.sourceAmount,\r
                1,\r
                block.timestamp + 60,                \r
                address(this)\r
            );   \r
    }\r
\r
\r
\r
    function trySwapBancorController(\r
            TradeRoute memory route\r
        ) internal returns (uint256 quote) \r
    { \r
        ICarbonController.TradeAction[] memory tradeActions;\r
        tradeActions = new ICarbonController.TradeAction[](1);\r
        tradeActions[0] = ICarbonController.TradeAction({\r
            strategyId: route.customInt,\r
            amount: uint128(route.sourceAmount)\r
        }); \r
\r
        uint256 val = _prepSwap(address(route.sourceToken), address(_iCarbonController), route.sourceAmount); \r
        quote = _iCarbonController.tradeBySourceAmount{ value: val }(\r
                Token(address(route.sourceToken)),\r
                Token(address(route.targetToken)),\r
                tradeActions,\r
                route.deadline,\r
                uint128(1)\r
            );         \r
    }\r
\r
    function tryUniswapV4(\r
            TradeRoute memory route\r
        ) internal returns (uint256 quote) \r
    {      \r
        IUniswapV4Helper.SwapData memory data = _IUniswapV4Helper.buildSwapData(route);\r
\r
        if (data.inputToken != address(0)) {\r
            try this.approveAndPermit(data.inputToken, route.sourceAmount) {\r
            } catch {\r
                return 2;    \r
            }\r
        }\r
        uint256 deadline = block.timestamp + 60;\r
        UNIVERSAL_ROUTER.execute{value: data.value}(data.commands, data.inputs, deadline);\r
        \r
        quote = data.outputToken == address(0) \r
                    ? address(this).balance\r
                    : IERC20(data.outputToken).balanceOf(address(this));\r
        return quote;   \r
    }    \r
\r
\r
    function trade(TradeRoute memory route) public payable returns (uint256 ret) {\r
        if (route.platformId == 10) { \r
            if (address(route.sourceToken) == NATIVE_TOKEN && address(route.targetToken) == ETH_TOKEN) {\r
                IWETH(ETH_TOKEN).deposit{ value: route.sourceAmount }();\r
            } else if (address(route.sourceToken) == ETH_TOKEN && address(route.targetToken) == NATIVE_TOKEN) {\r
                IWETH(ETH_TOKEN).withdraw(route.sourceAmount);\r
            } else {\r
                revert InvalidWethTrade();\r
            }  \r
            return route.sourceAmount;\r
        } \r
        else if (route.platformId == 1) {  \r
            ret = tryBancorV2Swap(route);\r
            return ret;\r
        } \r
        else if (route.platformId == 2) {  \r
            ret = tryBancorV3Swap(route);\r
            return ret;\r
        } \r
        else if (route.platformId == 66) {  \r
            ret = trySwapBancorController(route);\r
            return ret;\r
        } \r
        else if (route.platformId == 8) {  \r
            ret = tryBancorPOLSwap(route);\r
            return ret;\r
        } \r
        else if (route.platformId == 11) {  \r
            ret = tryGSPSwap(route);\r
            return ret;\r
        }\r
        else if (route.platformId == 14) {  \r
            ret = tryUniswapV4(route);\r
            return ret;\r
        } \r
        else if (route.platformId == 16) {  \r
            ret = tryHyperSwap(route);\r
            return ret;\r
        } \r
        else if (route.platformId == 17) {  \r
            ret = tryMooniSwap(route);\r
            return ret;\r
        }                \r
        else\r
        {\r
            address executorAddress = getsetExecutor(route.platformId, address(0)); \r
            if (executorAddress == address(0)) revert UnsupportedPlatform();\r
          \r
            if(getsetDelegatePlatform (route.platformId, false, false)) {\r
\r
                address customAddress = _platformRouterOverride[route.platformId];\r
                if (customAddress == address(0)) {\r
                    customAddress = route.customAddress;\r
                }\r
                _approveAndTransfer(\r
                    address(route.sourceToken),\r
                    customAddress, \r
                    route.sourceAmount,\r
                    false \r
                );        \r
                ret = _delegateTrySwap(executorAddress, route);\r
            }\r
            else \r
            {\r
                ret = 25; // ret = IExecutor(executorAddress).trySwap(route);  // direct call, no try/catch    \r
            }             \r
\r
            return ret;\r
        }\r
    }\r
\r
    function _delegateTrySwap(address impl, TradeRoute memory route) internal returns (uint256 ret) {\r
        bytes memory data = abi.encodeWithSelector(IExecutor.trySwap.selector, route);\r
        (, bytes memory r) = impl.delegatecall(data);\r
        ret = abi.decode(r, (uint256));\r
    }    \r
\r
    function _approveAndTransfer(\r
        address token,\r
        address to_approve_address,\r
        uint256 amount,\r
        bool transfer\r
    ) internal {\r
        if(token == NATIVE_TOKEN){\r
            return;\r
        }  \r
        if (!_approved[token][to_approve_address]) {\r
            IERC20(token).forceApprove(to_approve_address, type(uint256).max);\r
            _approved[token][to_approve_address] = true;\r
        }\r
        if(transfer){\r
            IERC20(token).safeTransfer(to_approve_address, amount);\r
        }        \r
    } \r
\r
    function approveAndPermit(address inputTokenAddr, uint256 amountIn) external {\r
        _approveAndTransfer(inputTokenAddr, address(PERMIT2), amountIn, false); \r
        PERMIT2.approve(\r
            inputTokenAddr,\r
            address(UNIVERSAL_ROUTER),\r
            uint160(amountIn),\r
            uint48(block.timestamp + 60)\r
        );       \r
    }\r
\r
    function _uncheckedInc(uint256 i) private pure returns (uint256 j) {\r
        unchecked {\r
            j = i + 1;\r
        }\r
    } \r
\r
    function _prepSwap(\r
        address token,\r
        address spender,\r
        uint256 amount\r
    ) internal returns (uint256 value) {\r
        if (token == NATIVE_TOKEN) {\r
            value = amount;\r
        } else {\r
            _approveAndTransfer(token, spender, amount, false);\r
        }\r
    }\r
}"
    },
    "contracts/executors/IExecutor.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.0;\r
import "./TokenStructs.sol";\r
\r
interface IExecutor {\r
    /**\r
     * @notice Attempts a swap using platform-specific logic.\r
     * @param route The trade route containing all necessary parameters.\r
     * @return result A uint256 result code or value, depending on implementation.\r
     */\r
    function trySwap(TradeRoute calldata route) external payable returns (uint256 result);\r
}\r
"
    },
    "https://github.com/Uniswap/permit2/src/interfaces/IPermit2.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ISignatureTransfer} from "./ISignatureTransfer.sol";
import {IAllowanceTransfer} from "./IAllowanceTransfer.sol";

/// @notice Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer.
/// @dev Users must approve Permit2 before calling any of the transfer functions.
interface IPermit2 is ISignatureTransfer, IAllowanceTransfer {
// IPermit2 unifies the two interfaces so users have maximal flexibility with their approval.
}
"
    },
    "contracts/src/IUniversalRouter.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later\r
pragma solidity ^0.8.24;\r
\r
interface IUniversalRouter {\r
    /// @notice Thrown when a required command has failed\r
    error ExecutionFailed(uint256 commandIndex, bytes message);\r
\r
    /// @notice Thrown when attempting to send ETH directly to the contract\r
    error ETHNotAccepted();\r
\r
    /// @notice Thrown when executing commands with an expired deadline\r
    error TransactionDeadlinePassed();\r
\r
    /// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided\r
    error LengthMismatch();\r
\r
    // @notice Thrown when an address that isn't WETH tries to send ETH to the router without calldata\r
    error InvalidEthSender();\r
\r
    /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\r
    /// @param commands A set of concatenated commands, each 1 byte in length\r
    /// @param inputs An array of byte strings containing abi encoded inputs for each command\r
    /// @param deadline The deadline by which the transaction must be executed\r
    function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;\r
}"
    },
    "contracts/exchanges/interfaces/ICarbonPOL.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import { Token } from "../../token/Token.sol";

/**
 * @notice CarbonPOL interface
 */
interface ICarbonPOL {

    /**
     * @notice returns the expected trade output (tokens received) given an token amount sent
     */
    function expectedTradeReturn(Token token, uint128 ethAmount) external view returns (uint128 tokenAmount);

    /**
     * @notice returns the expected trade input (how many tokens to send) given a token amount received
     */
    function expectedTradeInput(Token token, uint128 tokenAmount) external view returns (uint128 ethAmount);

    /**
     * @notice trades ETH for *amount* of token based on the current token price (trade by target amount)
     * @notice if token == ETH, trades BNT for amount of ETH
     */
    function trade(Token token, uint128 targetAmount, uint128 maxInput) external payable;

    function minTokenSaleAmount(Token token) external view returns (uint128);
    function minTokenSaleAmountMultiplier() external view returns (uint32);
    function amountAvailableForTrading(Token token) external view returns (uint128);
}"
    },
    "contracts/exchanges/interfaces/ICarbonController.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
pragma solidity 0.8.26;\r
\r
import { IERC20 } from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";\r
import { Token } from "contracts/token/Token.sol";\r
/**\r
 * Carbon controller interface\r
 */\r
interface ICarbonController {\r
    struct Strategy {\r
        uint256 id;\r
        address owner;\r
        Token[2] tokens;\r
        Order[2] orders;\r
    }\r
    struct Order {\r
        uint128 y;\r
        uint128 z;\r
        uint64 A;\r
        uint64 B;\r
    }\r
    struct TradeAction {\r
        uint256 strategyId;\r
        uint128 amount;\r
    }\r
\r
    struct Pair {\r
        uint128 id;\r
        Token[2] tokens;\r
    }\r
\r
    function pair(Token token0, Token token1) external view returns (Pair memory);\r
\r
    function strategiesByPair(\r
        Token token0,\r
        Token token1,\r
        uint256 startIndex,\r
        uint256 endIndex\r
    ) external view returns (Strategy[] memory);\r
\r
    function calculateTradeSourceAmount(\r
        Token sourceToken,\r
        Token targetToken,\r
        TradeAction[] calldata tradeActions\r
    ) external view returns (uint128);\r
\r
    function calculateTradeTargetAmount(\r
        Token sourceToken,\r
        Token targetToken,\r
        TradeAction[] calldata tradeActions\r
    ) external view returns (uint128);\r
\r
    function tradeBySourceAmount(\r
        Token sourceToken,\r
        Token targetToken,\r
        TradeAction[] calldata tradeActions,\r
        uint256 deadline,\r
        uint128 minReturn\r
    ) external payable returns (uint128);\r
    \r
    function tradeByTargetAmount(\r
        Token sourceToken,\r
        Token targetToken,\r
        TradeAction[] calldata tradeActions,\r
        uint256 deadline,\r
        uint128 maxInput\r
    ) external payable returns (uint128);\r
}"
    },
    "contracts/exchanges/interfaces/IBancorNetworkV2.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { IERC20 } from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";

import { Token } from "../../token/Token.sol";

/**
 * Bancor Network V2 interface
 */
interface IBancorNetworkV2 {
    function convertByPath(
        address[] memory _path,
        uint256 _amount,
        uint256 _minReturn,
        address _beneficiary,
        address _affiliateAccount,
        uint256 _affiliateFee
    ) external payable returns (uint256);

    function conversionPath(Token _sourceToken, Token _targetToken) external view returns (address[] memory);
}"
    },
    "contracts/exchanges/interfaces/IBancorNetwork.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { IERC20 } from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";

import { Token } from "../../token/Token.sol";

/**
 * @dev Flash-loan recipient interface
 */
interface IFlashLoanRecipient {
    /**
     * @dev a flash-loan recipient callback after each the caller must return the borrowed amount and an additional fee
     */
    function onFlashLoan(
        address caller,
        IERC20 erc20Token,
        uint256 amount,
        uint256 feeAmount,
        bytes memory data
    ) external;
}

/**
 * @dev Bancor Network interface
 */
interface IBancorNetwork {
    /**
     * @dev returns the respective pool collection for the provided pool
     */
    function collectionByPool(Token pool) external view returns (address);
    
    function tradeBySourceAmount(
        Token sourceToken,
        Token targetToken,
        uint256 sourceAmount,
        uint256 minReturnAmount,
        uint256 deadline,
        address beneficiary
    ) external payable returns (uint256);

    
    function tradeByTargetAmount(
        Token sourceToken,
        Token targetToken,
        uint256 targetAmount,
        uint256 maxSourceAmount,
        uint256 deadline,
        address beneficiary
    ) external payable returns (uint256);

    /**
     * @dev provides a flash-loan
     *
     * requirements:
     *
     * - the recipient's callback must return *at least* the borrowed amount and fee back to the specified return address
     */
    function flashLoan(Token token, uint256 amount, IFlashLoanRecipient recipient, bytes calldata data) external;
}
"
    },
    "contracts/vault/BalancerUtils.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { IERC20 as IStandardERC20 } from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
import { IERC20 as IBalancerERC20 } from "./IFlashLoanRecipient.sol";

function castTokens(IStandardERC20[] memory inputTokens) pure returns (IBalancerERC20[] memory outputTokens) {
    // solhint-disable no-inline-assembly
    assembly {
        outputTokens := inputTokens
    }
    // solhint-enable no-inline-assembly
}"
    },
    "contracts/vault/IFlashLoanRecipient.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.7.0 <0.9.0;

// Inspired by Aave Protocol's IFlashLoanReceiver.

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IFlashLoanRecipient {
    /**
     * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient.
     *
     * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this
     * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the
     * Vault, or else the entire flash loan will revert.
     *
     * `userData` is the same value passed in the `IVault.flashLoan` call.
     */
    function receiveFlashLoan(
        IERC20[] memory tokens,
        uint256[] memory amounts,
        uint256[] memory feeAmounts,
        bytes memory userData
    ) external;
}"
    },
    "contracts/vault/IVault.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "contracts/vault/IAuthentication.sol";
import "contracts/vault/ISignaturesValidator.sol";
import "contracts/vault/ITemporarilyPausable.sol";
//interface IWETH {
//    function deposit() external payable;
//    function transfer(address to, uint value) external returns (bool);
//    function withdraw(uint) external;
//}

import "contracts/vault/IAsset.sol";
import "contracts/vault/IAuthorizer.sol";
import "contracts/vault/IFlashLoanRecipient.sol";
import "contracts/vault/IProtocolFeesCollector.sol";

pragma solidity >=0.7.0 <0.9.0;

/**
 * @dev Full external interface for the Vault core contract - no external or public methods exist in the contract that
 * don't override one of these declarations.
 */
interface IVault is ISignaturesValidator, ITemporarilyPausable, IAuthentication {
    // Generalities about the Vault:
    //
    // - Whenever documentation refers to 'tokens', it strictly refers to ERC20-compliant token contracts. Tokens are
    // transferred out of the Vault by calling the `IERC20.transfer` function, and transferred in by calling
    // `IERC20.transferFrom`. In these cases, the sender must have previously allowed the Vault to use their tokens by
    // calling `IERC20.approve`. The only deviation from the ERC20 standard that is supported is functions not returning
    // a boolean value: in these scenarios, a non-reverting call is assumed to be successful.
    //
    // - All non-view functions in the Vault are non-reentrant: calling them while another one is mid-execution (e.g.
    // while execution control is transferred to a token contract during a swap) will result in a revert. View
    // functions can be called in a re-reentrant way, but doing so might cause them to return inconsistent results.
    // Contracts calling view functions in the Vault must make sure the Vault has not already been entered.
    //
    // - View functions revert if referring to either unregistered Pools, or unregistered tokens for registered Pools.

    // Authorizer
    //
    // Some system actions are permissioned, like setting and collecting protocol fees. This permissioning system exists
    // outside of the Vault in the Authorizer contract: the Vault simply calls the Authorizer to check if the caller
    // can perform a given action.

    /**
     * @dev Returns the Vault's Authorizer.
     */
    function getAuthorizer() external view returns (IAuthorizer);

    /**
     * @dev Sets a new Authorizer for the Vault. The caller must be allowed by the current Authorizer to do this.
     *
     * Emits an `AuthorizerChanged` event.
     */
    function setAuthorizer(IAuthorizer newAuthorizer) external;

    /**
     * @dev Emitted when a new authorizer is set by `setAuthorizer`.
     */
    event AuthorizerChanged(IAuthorizer indexed newAuthorizer);

    // Relayers
    //
    // Additionally, it is possible for an account to perform certain actions on behalf of another one, using their
    // Vault ERC20 allowance and Internal Balance. These accounts are said to be 'relayers' for these Vault functions,
    // and are expected to be smart contracts with sound authentication mechanisms. For an account to be able to wield
    // this power, two things must occur:
    //  - The Authorizer must grant the account the permission to be a relayer for the relevant Vault function. This
    //    means that Balancer governance must approve each individual contract to act as a relayer for the intended
    //    functions.
    //  - Each user must approve the relayer to act on their behalf.
    // This double protection means users cannot be tricked into approving malicious relayers (because they will not
    // have been allowed by the Authorizer via governance), nor can malicious relayers approved by a compromised
    // Authorizer or governance drain user funds, since they would also need to be approved by each individual user.

    /**
     * @dev Returns true if `user` has approved `relayer` to act as a relayer for them.
     */
    function hasApprovedRelayer(address user, address relayer) external view returns (bool);

    /**
     * @dev Allows `relayer` to act as a relayer for `sender` if `approved` is true, and disallows it otherwise.
     *
     * Emits a `RelayerApprovalChanged` event.
     */
    function setRelayerApproval(
        address sender,
        address relayer,
        bool approved
    ) external;

    /**
     * @dev Emitted every time a relayer is approved or disapproved by `setRelayerApproval`.
     */
    event RelayerApprovalChanged(address indexed relayer, address indexed sender, bool approved);

    // Internal Balance
    //
    // Users can deposit tokens into the Vault, where they are allocated to their Internal Balance, and later
    // transferred or withdrawn. It can also be used as a source of tokens when joining Pools, as a destination
    // when exiting them, and as either when performing swaps. This usage of Internal Balance results in greatly reduced
    // gas costs when compared to relying on plain ERC20 transfers, leading to large savings for frequent users.
    //
    // Internal Balance management features batching, which means a single contract call can be used to perform multiple
    // operations of different kinds, with different senders and recipients, at once.

    /**
     * @dev Returns `user`'s Internal Balance for a set of tokens.
     */
    function getInternalBalance(address user, IERC20[] memory tokens) external view returns (uint256[] memory);

    /**
     * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)
     * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as
     * it lets integrators reuse a user's Vault allowance.
     *
     * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.
     */
    function manageUserBalance(UserBalanceOp[] memory ops) external payable;

    /**
     * @dev Data for `manageUserBalance` operations, which include the possibility for ETH to be sent and received
     without manual WETH wrapping or unwrapping.
     */
    struct UserBalanceOp {
        UserBalanceOpKind kind;
        IAsset asset;
        uint256 amount;
        address sender;
        address payable recipient;
    }

    // There are four possible operations in `manageUserBalance`:
    //
    // - DEPOSIT_INTERNAL
    // Increases the Internal Balance of the `recipient` account by transferring tokens from the corresponding
    // `sender`. The sender must have allowed the Vault to use their tokens via `IERC20.approve()`.
    //
    // ETH can be used by passing the ETH sentinel value as the asset and forwarding ETH in the call: it will be wrapped
    // and deposited as WETH. Any ETH amount remaining will be sent back to the caller (not the sender, which is
    // relevant for relayers).
    //
    // Emits an `InternalBalanceChanged` event.
    //
    //
    // - WITHDRAW_INTERNAL
    // Decreases the Internal Balance of the `sender` account by transferring tokens to the `recipient`.
    //
    // ETH can be used by passing the ETH sentinel value as the asset. This will deduct WETH instead, unwrap it and send
    // it to the recipient as ETH.
    //
    // Emits an `InternalBalanceChanged` event.
    //
    //
    // - TRANSFER_INTERNAL
    // Transfers tokens from the Internal Balance of the `sender` account to the Internal Balance of `recipient`.
    //
    // Reverts if the ETH sentinel value is passed.
    //
    // Emits an `InternalBalanceChanged` event.
    //
    //
    // - TRANSFER_EXTERNAL
    // Transfers tokens from `sender` to `recipient`, using the Vault's ERC20 allowance. This is typically used by
    // relayers, as it lets them reuse a user's Vault allowance.
    //
    // Reverts if the ETH sentinel value is passed.
    //
    // Emits an `ExternalBalanceTransfer` event.

    enum UserBalanceOpKind { DEPOSIT_INTERNAL, WITHDRAW_INTERNAL, TRANSFER_INTERNAL, TRANSFER_EXTERNAL }

    /**
     * @dev Emitted when a user's Internal Balance changes, either from calls to `manageUserBalance`, or through
     * interacting with Pools using Internal Balance.
     *
     * Because Internal Balance works exclusively with ERC20 tokens, ETH deposits and withdrawals will use the WETH
     * address.
     */
    event InternalBalanceChanged(address indexed user, IERC20 indexed token, int256 delta);

    /**
     * @dev Emitted when a user's Vault ERC20 allowance is used by the Vault to transfer tokens to an external account.
     */
    event ExternalBalanceTransfer(IERC20 indexed token, address indexed sender, address recipient, uint256 amount);

    // Pools
    //
    // There are three specialization settings for Pools, which allow for cheaper swaps at the cost of reduced
    // functionality:
    //
    //  - General: no specialization, suited for all Pools. IGeneralPool is used for swap request callbacks, passing the
    // balance of all tokens in the Pool. These Pools have the largest swap costs (because of the extra storage reads),
    // which increase with the number of registered tokens.
    //
    //  - Minimal Swap Info: IMinimalSwapInfoPool is used instead of IGeneralPool, which saves gas by only passing the
    // balance of the two tokens involved in the swap. This is suitable for some pricing algorithms, like the weighted
    // constant product one popularized by Balancer V1. Swap costs are smaller compared to general Pools, and are
    // independent of the number of registered tokens.
    //
    //  - Two Token: only allows two tokens to be registered. This achieves the lowest possible swap gas cost. Like
    // minimal swap info Pools, these are called via IMinimalSwapInfoPool.

    enum PoolSpecialization { GENERAL, MINIMAL_SWAP_INFO, TWO_TOKEN }

    /**
     * @dev Registers the caller account as a Pool with a given specialization setting. Returns the Pool's ID, which
     * is used in all Pool-related functions. Pools cannot be deregistered, nor can the Pool's specialization be
     * changed.
     *
     * The caller is expected to be a smart contract that implements either `IGeneralPool` or `IMinimalSwapInfoPool`,
     * depending on the chosen specialization setting. This contract is known as the Pool's contract.
     *
     * Note that the same contract may register itself as multiple Pools with unique Pool IDs, or in other words,
     * multiple Pools may share the same contract.
     *
     * Emits a `PoolRegistered` event.
     */
    function registerPool(PoolSpecialization specialization) external returns (bytes32);

    /**
     * @dev Emitted when a Pool is registered by calling `registerPool`.
     */
    event PoolRegistered(bytes32 indexed poolId, address indexed poolAddress, PoolSpecialization specialization);

    /**
     * @dev Returns a Pool's contract address and specialization setting.
     */
    function getPool(bytes32 poolId) external view returns (address, PoolSpecialization);

    /**
     * @dev Registers `tokens` for the `poolId` Pool. Must be called by the Pool's contract.
     *
     * Pools can only interact with tokens they have registered. Users join a Pool by transferring registered tokens,
     * exit by receiving registered tokens, and can only swap registered tokens.
     *
     * Each token can only be registered once. For Pools with the Two Token specialization, `tokens` must have a length
     * of two, that is, both tokens must be registered in the same `registerTokens` call, and they must be sorted in
     * ascending order.
     *
     * The `tokens` and `assetManagers` arrays must have the same length, and each entry in these indicates the Asset
     * Manager for the corresponding token. Asset Managers can manage a Pool's tokens via `managePoolBalance`,
     * depositing and withdrawing them directly, and can even set their balance to arbitrary amounts. They are therefore
     * expected to be highly secured smart contracts with sound design principles, and the decision to register an
     * Asset Manager should not be made lightly.
     *
     * Pools can choose not to assign an Asset Manager to a given token by passing in the zero address. Once an Asset
     * Manager is set, it cannot be changed except by deregistering the associated token and registering again with a
     * different Asset Manager.
     *
     * Emits a `TokensRegistered` event.
     */
    function registerTokens(
        bytes32 poolId,
        IERC20[] memory tokens,
        address[] memory assetManagers
    ) external;

    /**
     * @dev Emitted when a Pool registers tokens by calling `registerTokens`.
     */
    event TokensRegistered(bytes32 indexed poolId, IERC20[] tokens, address[] assetManagers);

    /**
     * @dev Deregisters `tokens` for the `poolId` Pool. Must be called by the Pool's contract.
     *
     * Only registered tokens (via `registerTokens`) can be deregistered. Additionally, they must have zero total
     * balance. For Pools with the Two Token specialization, `tokens` must have a length of two, that is, both tokens
     * must be deregistered in the same `deregisterTokens` call.
     *
     * A deregistered token can be re-registered later on, possibly with a different Asset Manager.
     *
     * Emits a `TokensDeregistered` event.
     */
    function deregisterTokens(bytes32 poolId, IERC20[] memory tokens) external;

    /**
     * @dev Emitted when a Pool deregisters tokens by calling `deregisterTokens`.
     */
    event TokensDeregistered(bytes32 indexed poolId, IERC20[] tokens);

    /**
     * @dev Returns detailed information for a Pool's registered token.
     *
     * `cash` is the number of tokens the Vault currently holds for the Pool. `managed` is the number of tokens
     * withdrawn and held outside the Vault by the Pool's token Asset Manager. The Pool's total balance for `token`
     * equals the sum of `cash` and `managed`.
     *
     * Internally, `cash` and `managed` are stored using 112 bits. No action can ever cause a Pool's token `cash`,
     * `managed` or `total` balance to be greater than 2^112 - 1.
     *
     * `lastChangeBlock` is the number of the block in which `token`'s total balance was last modified (via either a
     * join, exit, swap, or Asset Manager update). This value is useful to avoid so-called 'sandwich attacks', for
     * example when developing price oracles. A change of zero (e.g. caused by a swap with amount zero) is considered a
     * change for this purpose, and will update `lastChangeBlock`.
     *
     * `assetManager` is the Pool's token Asset Manager.
     */
    function getPoolTokenInfo(bytes32 poolId, IERC20 token)
        external
        view
        returns (
            uint256 cash,
            uint256 managed,
            uint256 lastChangeBlock,
            address assetManager
        );

    /**
     * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of
     * the tokens' `balances` changed.
     *
     * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all
     * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.
     *
     * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same
     * order as passed to `registerTokens`.
     *
     * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are
     * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`
     * instead.
     */
    function getPoolTokens(bytes32 poolId)
        external
        view
        returns (
            IERC20[] memory tokens,
            uint256[] memory balances,
            uint256 lastChangeBlock
        );

    /**
     * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will
     * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized
     * Pool shares.
     *
     * If the caller is not `sender`, it must be an authorized relayer for them.
     *
     * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount
     * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces
     * these maximums.
     *
     * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable
     * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the
     * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent
     * back to the caller (not the sender, which is important for relayers).
     *
     * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when
     * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be
     * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final
     * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.
     *
     * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only
     * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be
     * withdrawn from Internal Balance: attempting to do so will trigger a revert.
     *
     * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement
     * their own custom logic. This typically requires additional information from the user (such as the expected number
     * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed
     * directly to the Pool's contract, as is `recipient`.
     *
     * Emits a `PoolBalanceChanged` event.
     */
    function joinPool(
        bytes32 poolId,
        address sender,
        address recipient,
        JoinPoolRequest memory request
    ) external payable;

    struct JoinPoolRequest {
        IAsset[] assets;
        uint256[] maxAmountsIn;
        bytes userData;
        bool fromInternalBalance;
    }

    /**
     * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will
     * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized
     * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see
     * `getPoolTokenInfo`).
     *
     * If the caller is not `sender`, it must be an authorized relayer for them.
     *
     * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum
     * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:
     * it just enforces these minimums.
     *
     * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To
     * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead
     * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.
     *
     * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when
     * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must
     * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the
     * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.
     *
     * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,
     * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to
     * do so will trigger a revert.
     *
     * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the
     * `tokens` array. This array must match the Pool's registered tokens.
     *
     * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement
     * their own custom logic. This typically requires additional information from the user (such as the expected number
     * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and
     * passed directly to the Pool's contract.
     *
     * Emits a `PoolBalanceChanged` event.
     */
    function exitPool(
        bytes32 poolId,
        address sender,
        address payable recipient,
        ExitPoolRequest memory request
    ) external;

    struct ExitPoolRequest {
        IAsset[] assets;
        uint256[] minAmountsOut;
        bytes userData;
        bool toInternalBalance;
    }

    /**
     * @dev Emitted when a user joins or exits a Pool by calling `joinPool` or `exitPool`, respectively.
     */
    event PoolBalanceChanged(
        bytes32 indexed poolId,
        address indexed liquidityProvider,
        IERC20[] tokens,
        int256[] deltas,
        uint256[] protocolFeeAmounts
    );

    enum PoolBalanceChangeKind { JOIN, EXIT }

    // Swaps
    //
    // Users can swap tokens with Pools by calling the `swap` and `batchSwap` functions. To do this,
    // they need not trust Pool contracts in any way: all security checks are made by the Vault. They must however be
    // aware of the Pools' pricing algorithms in order to estimate the prices Pools will quote.
    //
    // The `swap` function executes a single swap, while `batchSwap` can perform multiple swaps in sequence.
    // In each individual swap, tokens of one kind are sent from the sender to the Pool (this is the 'token in'),
    // and tokens of another kind are sent from the Pool to the recipient in exchange (this is the 'token out').
    // More complex swaps, such as one token in to multiple tokens out can be achieved by batching together
    // individual swaps.
    //
    // There are two swap kinds:
    //  - 'given in' swaps, where the amount of tokens in (sent to the Pool) is known, and the Pool determines (via the
    // `onSwap` hook) the amount of tokens out (to send to the recipient).
    //  - 'given out' swaps, where the amount of tokens out (received from the Pool) is known, and the Pool determines
    // (via the `onSwap` hook) the amount of tokens in (to receive from the sender).
    //
    // Additionally, it is possible to chain swaps using a placeholder input amount, which the Vault replaces with
    // the calculated output of the previous swap. If the previous swap was 'given in', this will be the calculated
    // tokenOut amount. If the previous swap was 'given out', it will use the calculated tokenIn amount. These extended
    // swaps are known as 'multihop' swaps, since they 'hop' through a number of intermediate tokens before arriving at
    // the final intended token.
    //
    // In all cases, tokens are only transferred in and out of the Vault (or withdrawn from and deposited into Internal
    // Balance) after all individual swaps have been completed, and the net token balance change computed. This makes
    // certain swap patterns, such as multihops, or swaps that interact with the same token pair in multiple Pools, cost
    // much less gas than they would otherwise.
    //
    // It also means that under certain conditions it is possible to perform arbitrage by swapping with multiple
    // Pools in a way that results in net token movement out of the Vault (profit), with no tokens being sent in (only
    // updating the Pool's internal accounting).
    //
    // To protect users from front-running or the market changing rapidly, they supply a list of 'limits' for each token
    // involved in the swap, where either the maximum number of tokens to send (by passing a positive value) or the
    // minimum amount of tokens to receive (by passing a negative value) is specified.
    //
    // Additionally, a 'deadline' timestamp can also be provided, forcing the swap to fail if it occurs after
    // this point in time (e.g. if the transaction failed to be included in a block promptly).
    //
    // If interacting with Pools that hold WETH, it is possible to both send and receive ETH directly: the Vault will do
    // the wrapping and unwrapping. To enable this mechanism, the IAsset sentinel value (the zero address) must be
    // passed in the `assets` array instead of the WETH address. Note that it is possible to combine ETH and WETH in the
    // same swap. Any excess ETH will be sent back to the caller (not the sender, which is relevant for relayers).
    //
    // Finally, Internal Balance can be used when either sending or receiving tokens.

    enum SwapKind { GIVEN_IN, GIVEN_OUT }

    /**
     * @dev Performs a swap with a single Pool.
     *
     * If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens
     * taken from the Pool, which must be greater than or equal to `limit`.
     *
     * If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens
     * sent to the Pool, which must be less than or equal to `limit`.
     *
     * Internal Balance usage and the recipient are determined by the `funds` struct.
     *
     * Emits a `Swap` event.
     */
    function swap(
        SingleSwap memory singleSwap,
        FundManagement memory funds,
        uint256 limit,
        uint256 deadline
    ) external payable returns (uint256);

    /**
     * @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on
     * the `kind` value.
     *
     * `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address).
     * Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault.
     *
     * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be
     * used to extend swap behavior.
     */
    struct SingleSwap {
        bytes32 poolId;
        SwapKind kind;
        IAsset assetIn;
        IAsset assetOut;
        uint256 amount;
        bytes userData;
    }

    /**
     * @dev Performs a series of swaps with one or multiple Pools. In each individual swap, the caller determines either
     * the amount of tokens sent to or received from the Pool, depending on the `kind` value.
     *
     * Returns an array with the net Vault asset balance deltas. Positive amounts represent tokens (or ETH) sent to the
     * Vault, and negative amounts represent tokens (or ETH) sent by the Vault. Each delta corresponds to the asset at
     * the same index in the `assets` array.
     *
     * Swaps are executed sequentially, in the order specified by the `swaps` array. Each array element describes a
     * Pool, the token to be sent to this Pool, the token to receive from it, and an amount that is either `amountIn` or
     * `amountOut` depending on the swap kind.
     *
     * Multihop swaps can be executed by passing an `amount` value of zero for a swap. This will cause the amount in/out
     * of the previous swap to be used as the amount in for the current one. In a 'given in' swap, 'tokenIn' must equal
     * the previous swap's `tokenOut`. For a 'given out' swap, `tokenOut` must equal the previous swap's `tokenIn`.
     *
     * The `assets` array contains the addresses of all assets involved in the swaps. These are either token addresses,
     * or the IAsset sentinel value for ETH (the zero address). Each entry in the `swaps` array specifies tokens in and
     * out by referencing an index in `assets`. Note that Pools never interact with ETH directly: it will be wrapped to
     * or unwrapped from WETH by the Vault.
     *
     * Internal Balance usage, sender, and recipient are determined by the `funds` struct. The `limits` array specifies
     * the minimum or maximum amount of each token the vault is allowed to transfer.
     *
     * `batchSwap` can be used to make a single swap, like `swap` does, but doing so requires more gas than the
     * equivalent `swap` call.
     *
     * Emits `Swap` events.
     */
    function batchSwap(
        SwapKind kind,
        BatchSwapStep[] memory swaps,
        IAsset[] memory assets,
        FundManagement memory funds,
        int256[] memory limits,
        uint256 deadline
    ) external payable returns (int256[] memory);

    /**
     * @dev Data for each individual swap executed by `batchSwap`. The asset in and out fields are indexes into the
     * `assets` array passed to that function, and ETH assets are converted to WETH.
     *
     * If `amount` is zero, the multihop mechanism is used to determine the ac

Tags:
ERC20, ERC165, Multisig, Swap, Liquidity, Yield, Voting, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xeec4c0f7ac21894130f110772b3e0db7a55d0ca0|verified:true|block:23680777|tx:0xa89dafa692df2e90030fa7e61a0d5e5bd06e55d7fab9cd2d9d269e0528311dc8|first_check:1761731146

Submitted on: 2025-10-29 10:45:46

Comments

Log in to comment.

No comments yet.