creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/swapper/adapters/BalancerV2Swap.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { LibAdapter } from \"src/libs/LibAdapter.sol\";\nimport { IVault } from \"src/interfaces/external/balancer/IVault.sol\";\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\nimport { IBasePool } from \"src/interfaces/external/balancer/IBasePool.sol\";\nimport { BaseAdapter, ISyncSwapper } from \"src/swapper/adapters/BaseAdapter.sol\";\n\ncontract BalancerV2Swap is BaseAdapter {\n    IVault public immutable vault;\n\n    constructor(address _router, address _balancerVault) BaseAdapter(_router) {\n        Errors.verifyNotZero(_balancerVault, \"_balancerVault\");\n        vault = IVault(_balancerVault);\n    }\n\n    /// @inheritdoc ISyncSwapper\n    function validate(address fromAddress, ISwapRouter.SwapData memory swapData) external view override {\n        bytes32 poolId = abi.decode(swapData.data, (bytes32));\n        bytes32 id = IBasePool(swapData.pool).getPoolId();\n\n        // verify that the swapData.pool has the same id as the encoded poolId\n        if (id != poolId) revert DataMismatch(\"poolId\");\n\n        // verify that the fromAddress and toAddress are in the pool. getPoolTokenInfo will revert if not\n        // slither-disable-start low-level-calls,missing-zero-check,unchecked-lowlevel\n\n        string memory funcSelector = \"getPoolTokenInfo(bytes32,address)\";\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success,) = address(vault).staticcall(abi.encodeWithSignature(funcSelector, poolId, fromAddress));\n        if (!success) revert DataMismatch(\"fromAddress\");\n\n        (success,) = address(vault).staticcall(abi.encodeWithSignature(funcSelector, poolId, swapData.token));\n        if (!success) revert DataMismatch(\"toAddress\");\n        // slither-disable-end low-level-calls,missing-zero-check,unchecked-lowlevel\n    }\n\n    /// @inheritdoc ISyncSwapper\n    function swap(\n        address,\n        address sellTokenAddress,\n        uint256 sellAmount,\n        address buyTokenAddress,\n        uint256 minBuyAmount,\n        bytes memory data\n    ) external override onlyRouter returns (uint256) {\n        bytes32 poolId = abi.decode(data, (bytes32));\n\n        IVault.SingleSwap memory singleSwap =\n            IVault.SingleSwap(poolId, IVault.SwapKind.GIVEN_IN, sellTokenAddress, buyTokenAddress, sellAmount, \"\");\n\n        IVault.FundManagement memory funds = IVault.FundManagement(address(this), false, payable(address(this)), false);\n\n        LibAdapter._approve(IERC20(sellTokenAddress), address(vault), sellAmount);\n\n        return vault.swap(singleSwap, funds, minBuyAmount, block.timestamp);\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "src/utils/Errors.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Address } from \"openzeppelin-contracts/utils/Address.sol\";\n\nlibrary Errors {\n    using Address for address;\n    ///////////////////////////////////////////////////////////////////\n    //                       Set errors\n    ///////////////////////////////////////////////////////////////////\n\n    error AccessDenied();\n    error ZeroAddress(string paramName);\n    error ZeroAmount();\n    error InsufficientBalance(address token);\n    error AssetNotAllowed(address token);\n    error NotImplemented();\n    error InvalidAddress(address addr);\n    error InvalidParam(string paramName);\n    error InvalidParams();\n    error UnsafePrice(address token, uint256 spotPrice, uint256 safePrice);\n    error AlreadySet(string param);\n    error AlreadyRegistered(address param);\n    error SlippageExceeded(uint256 expected, uint256 actual);\n    error ArrayLengthMismatch(uint256 length1, uint256 length2, string details);\n\n    error ItemNotFound();\n    error ItemExists();\n    error MissingRole(bytes32 role, address user);\n    error RegistryItemMissing(string item);\n    error NotRegistered();\n    // Used to check storage slot is empty before setting.\n    error MustBeZero();\n    // Used to check storage slot set before deleting.\n    error MustBeSet();\n\n    error ApprovalFailed(address token);\n    error FlashLoanFailed(address token, uint256 amount);\n\n    error SystemMismatch(address source1, address source2);\n\n    error InvalidToken(address token);\n    error UnreachableError();\n\n    error InvalidSigner(address signer);\n\n    error InvalidChainId(uint256 chainId);\n\n    error SenderMismatch(address recipient, address sender);\n\n    function verifyNotZero(address addr, string memory paramName) internal pure {\n        if (addr == address(0)) {\n            revert ZeroAddress(paramName);\n        }\n    }\n\n    function verifyNotZero(bytes32 key, string memory paramName) internal pure {\n        if (key == bytes32(0)) {\n            revert InvalidParam(paramName);\n        }\n    }\n\n    function verifyNotEmpty(string memory val, string memory paramName) internal pure {\n        if (bytes(val).length == 0) {\n            revert InvalidParam(paramName);\n        }\n    }\n\n    function verifyNotZero(uint256 num, string memory paramName) internal pure {\n        if (num == 0) {\n            revert InvalidParam(paramName);\n        }\n    }\n\n    function verifySystemsMatch(address component1, address component2) internal view {\n        bytes memory call = abi.encodeWithSignature(\"getSystemRegistry()\");\n\n        address registry1 = abi.decode(component1.functionStaticCall(call), (address));\n        address registry2 = abi.decode(component2.functionStaticCall(call), (address));\n\n        if (registry1 != registry2) {\n            revert SystemMismatch(component1, component2);\n        }\n    }\n\n    function verifyArrayLengths(uint256 length1, uint256 length2, string memory details) internal pure {\n        if (length1 != length2) {\n            revert ArrayLengthMismatch(length1, length2, details);\n        }\n    }\n}\n"
      },
      "src/libs/LibAdapter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol\";\n\nlibrary LibAdapter {\n    using SafeERC20 for IERC20;\n\n    address public constant CURVE_REGISTRY_ETH_ADDRESS_POINTER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n    error MinLpAmountNotReached();\n    error LpTokenAmountMismatch();\n    error NoNonZeroAmountProvided();\n    error InvalidBalanceChange();\n\n    // Utils\n    function _approve(IERC20 token, address spender, uint256 amount) internal {\n        uint256 currentAllowance = token.allowance(address(this), spender);\n        if (currentAllowance > 0) {\n            token.safeDecreaseAllowance(spender, currentAllowance);\n        }\n        token.safeIncreaseAllowance(spender, amount);\n    }\n}\n"
      },
      "src/interfaces/external/balancer/IVault.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.17;\n\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\nimport { IProtocolFeesCollector } from \"src/interfaces/external/balancer/IProtocolFeesCollector.sol\";\nimport { IAsset } from \"src/interfaces/external/balancer/IAsset.sol\";\n\n/**\n * @dev Full external interface for the Vault core contract - no external or public methods exist in the contract that\n * don't override one of these declarations.\n */\ninterface IVault {\n    // Pools\n    //\n    // There are three specialization settings for Pools, which allow for cheaper swaps at the cost of reduced\n    // functionality:\n    //\n    //  - General: no specialization, suited for all Pools. IGeneralPool is used for swap request callbacks, passing the\n    // balance of all tokens in the Pool. These Pools have the largest swap costs (because of the extra storage reads),\n    // which increase with the number of registered tokens.\n    //\n    //  - Minimal Swap Info: IMinimalSwapInfoPool is used instead of IGeneralPool, which saves gas by only passing the\n    // balance of the two tokens involved in the swap. This is suitable for some pricing algorithms, like the weighted\n    // constant product one popularized by Balancer V1. Swap costs are smaller compared to general Pools, and are\n    // independent of the number of registered tokens.\n    //\n    //  - Two Token: only allows two tokens to be registered. This achieves the lowest possible swap gas cost. Like\n    // minimal swap info Pools, these are called via IMinimalSwapInfoPool.\n\n    enum PoolSpecialization {\n        GENERAL,\n        MINIMAL_SWAP_INFO,\n        TWO_TOKEN\n    }\n\n    enum JoinKind {\n        INIT,\n        EXACT_TOKENS_IN_FOR_BPT_OUT,\n        TOKEN_IN_FOR_EXACT_BPT_OUT,\n        ALL_TOKENS_IN_FOR_EXACT_BPT_OUT\n    }\n\n    /**\n     * @dev Data for `manageUserBalance` operations, which include the possibility for ETH to be sent and received\n     *  without manual WETH wrapping or unwrapping.\n     */\n    struct UserBalanceOp {\n        UserBalanceOpKind kind;\n        address asset;\n        uint256 amount;\n        address sender;\n        address payable recipient;\n    }\n\n    // There are four possible operations in `manageUserBalance`:\n    //\n    // - DEPOSIT_INTERNAL\n    // Increases the Internal Balance of the `recipient` account by transferring tokens from the corresponding\n    // `sender`. The sender must have allowed the Vault to use their tokens via `IERC20.approve()`.\n    //\n    // ETH can be used by passing the ETH sentinel value as the asset and forwarding ETH in the call: it will be wrapped\n    // and deposited as WETH. Any ETH amount remaining will be sent back to the caller (not the sender, which is\n    // relevant for relayers).\n    //\n    // Emits an `InternalBalanceChanged` event.\n    //\n    //\n    // - WITHDRAW_INTERNAL\n    // Decreases the Internal Balance of the `sender` account by transferring tokens to the `recipient`.\n    //\n    // ETH can be used by passing the ETH sentinel value as the asset. This will deduct WETH instead, unwrap it and send\n    // it to the recipient as ETH.\n    //\n    // Emits an `InternalBalanceChanged` event.\n    //\n    //\n    // - TRANSFER_INTERNAL\n    // Transfers tokens from the Internal Balance of the `sender` account to the Internal Balance of `recipient`.\n    //\n    // Reverts if the ETH sentinel value is passed.\n    //\n    // Emits an `InternalBalanceChanged` event.\n    //\n    //\n    // - TRANSFER_EXTERNAL\n    // Transfers tokens from `sender` to `recipient`, using the Vault's ERC20 allowance. This is typically used by\n    // relayers, as it lets them reuse a user's Vault allowance.\n    //\n    // Reverts if the ETH sentinel value is passed.\n    //\n    // Emits an `ExternalBalanceTransfer` event.\n\n    enum UserBalanceOpKind {\n        DEPOSIT_INTERNAL,\n        WITHDRAW_INTERNAL,\n        TRANSFER_INTERNAL,\n        TRANSFER_EXTERNAL\n    }\n\n    /**\n     * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n     * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n     * it lets integrators reuse a user's Vault allowance.\n     *\n     * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n     */\n    function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n    /**\n     * @dev Returns a Pool's contract address and specialization setting.\n     */\n    function getPool(bytes32 poolId) external view returns (address, PoolSpecialization);\n\n    /**\n     * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n     * the tokens' `balances` changed.\n     *\n     * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n     * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n     *\n     * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n     * order as passed to `registerTokens`.\n     *\n     * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n     * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n     * instead.\n     */\n    function getPoolTokens(bytes32 poolId)\n        external\n        view\n        returns (IERC20[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock);\n\n    /**\n     * @dev Returns detailed information for a Pool's registered token.\n     *\n     * `cash` is the number of tokens the Vault currently holds for the Pool. `managed` is the number of tokens\n     * withdrawn and held outside the Vault by the Pool's token Asset Manager. The Pool's total balance for `token`\n     * equals the sum of `cash` and `managed`.\n     *\n     * Internally, `cash` and `managed` are stored using 112 bits. No action can ever cause a Pool's token `cash`,\n     * `managed` or `total` balance to be greater than 2^112 - 1.\n     *\n     * `lastChangeBlock` is the number of the block in which `token`'s total balance was last modified (via either a\n     * join, exit, swap, or Asset Manager update). This value is useful to avoid so-called 'sandwich attacks', for\n     * example when developing price oracles. A change of zero (e.g. caused by a swap with amount zero) is considered a\n     * change for this purpose, and will update `lastChangeBlock`.\n     *\n     * `assetManager` is the Pool's token Asset Manager.\n     */\n    function getPoolTokenInfo(\n        bytes32 poolId,\n        IERC20 token\n    ) external view returns (uint256 cash, uint256 managed, uint256 lastChangeBlock, address assetManager);\n\n    /**\n     * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n     * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n     * Pool shares.\n     *\n     * If the caller is not `sender`, it must be an authorized relayer for them.\n     *\n     * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n     * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n     * these maximums.\n     *\n     * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n     * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n     * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n     * back to the caller (not the sender, which is important for relayers).\n     *\n     * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n     * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n     * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n     * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n     *\n     * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n     * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n     * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n     *\n     * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n     * their own custom logic. This typically requires additional information from the user (such as the expected number\n     * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n     * directly to the Pool's contract, as is `recipient`.\n     *\n     * Emits a `PoolBalanceChanged` event.\n     */\n    function joinPool(\n        bytes32 poolId,\n        address sender,\n        address recipient,\n        JoinPoolRequest memory request\n    ) external payable;\n\n    struct JoinPoolRequest {\n        address[] assets;\n        uint256[] maxAmountsIn;\n        bytes userData;\n        bool fromInternalBalance;\n    }\n\n    /**\n     * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n     * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n     * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n     * `getPoolTokenInfo`).\n     *\n     * If the caller is not `sender`, it must be an authorized relayer for them.\n     *\n     * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n     * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n     * it just enforces these minimums.\n     *\n     * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n     * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n     * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n     *\n     * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n     * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n     * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n     * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n     *\n     * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n     * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n     * do so will trigger a revert.\n     *\n     * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n     * `tokens` array. This array must match the Pool's registered tokens.\n     *\n     * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n     * their own custom logic. This typically requires additional information from the user (such as the expected number\n     * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n     * passed directly to the Pool's contract.\n     *\n     * Emits a `PoolBalanceChanged` event.\n     */\n    function exitPool(\n        bytes32 poolId,\n        address sender,\n        address payable recipient,\n        ExitPoolRequest memory request\n    ) external;\n\n    struct ExitPoolRequest {\n        address[] assets;\n        uint256[] minAmountsOut;\n        bytes userData;\n        bool toInternalBalance;\n    }\n\n    /**\n     * @dev Data for each individual swap executed by `batchSwap`. The asset in and out fields are indexes into the\n     * `assets` array passed to that function, and ETH assets are converted to WETH.\n     *\n     * If `amount` is zero, the multihop mechanism is used to determine the actual amount based on the amount in/out\n     * from the previous swap, depending on the swap kind.\n     *\n     * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be\n     * used to extend swap behavior.\n     */\n    struct BatchSwapStep {\n        bytes32 poolId;\n        uint256 assetInIndex;\n        uint256 assetOutIndex;\n        uint256 amount;\n        bytes userData;\n    }\n\n    /**\n     * @dev Emitted for each individual swap performed by `swap` or `batchSwap`.\n     */\n    event Swap(\n        bytes32 indexed poolId, IERC20 indexed tokenIn, IERC20 indexed tokenOut, uint256 amountIn, uint256 amountOut\n    );\n\n    // Swaps\n    //\n    // Users can swap tokens with Pools by calling the `swap` and `batchSwap` functions. To do this,\n    // they need not trust Pool contracts in any way: all security checks are made by the Vault. They must however be\n    // aware of the Pools' pricing algorithms in order to estimate the prices Pools will quote.\n    //\n    // The `swap` function executes a single swap, while `batchSwap` can perform multiple swaps in sequence.\n    // In each individual swap, tokens of one kind are sent from the sender to the Pool (this is the 'token in'),\n    // and tokens of another kind are sent from the Pool to the recipient in exchange (this is the 'token out').\n    // More complex swaps, such as one token in to multiple tokens out can be achieved by batching together\n    // individual swaps.\n    //\n    // There are two swap kinds:\n    //  - 'given in' swaps, where the amount of tokens in (sent to the Pool) is known, and the Pool determines (via the\n    // `onSwap` hook) the amount of tokens out (to send to the recipient).\n    //  - 'given out' swaps, where the amount of tokens out (received from the Pool) is known, and the Pool determines\n    // (via the `onSwap` hook) the amount of tokens in (to receive from the sender).\n    //\n    // Additionally, it is possible to chain swaps using a placeholder input amount, which the Vault replaces with\n    // the calculated output of the previous swap. If the previous swap was 'given in', this will be the calculated\n    // tokenOut amount. If the previous swap was 'given out', it will use the calculated tokenIn amount. These extended\n    // swaps are known as 'multihop' swaps, since they 'hop' through a number of intermediate tokens before arriving at\n    // the final intended token.\n    //\n    // In all cases, tokens are only transferred in and out of the Vault (or withdrawn from and deposited into Internal\n    // Balance) after all individual swaps have been completed, and the net token balance change computed. This makes\n    // certain swap patterns, such as multihops, or swaps that interact with the same token pair in multiple Pools, cost\n    // much less gas than they would otherwise.\n    //\n    // It also means that under certain conditions it is possible to perform arbitrage by swapping with multiple\n    // Pools in a way that results in net token movement out of the Vault (profit), with no tokens being sent in (only\n    // updating the Pool's internal accounting).\n    //\n    // To protect users from front-running or the market changing rapidly, they supply a list of 'limits' for each token\n    // involved in the swap, where either the maximum number of tokens to send (by passing a positive value) or the\n    // minimum amount of tokens to receive (by passing a negative value) is specified.\n    //\n    // Additionally, a 'deadline' timestamp can also be provided, forcing the swap to fail if it occurs after\n    // this point in time (e.g. if the transaction failed to be included in a block promptly).\n    //\n    // If interacting with Pools that hold WETH, it is possible to both send and receive ETH directly: the Vault will do\n    // the wrapping and unwrapping. To enable this mechanism, the IAsset sentinel value (the zero address) must be\n    // passed in the `assets` array instead of the WETH address. Note that it is possible to combine ETH and WETH in the\n    // same swap. Any excess ETH will be sent back to the caller (not the sender, which is relevant for relayers).\n    //\n    // Finally, Internal Balance can be used when either sending or receiving tokens.\n\n    enum SwapKind {\n        GIVEN_IN,\n        GIVEN_OUT\n    }\n\n    /**\n     * @dev Performs a swap with a single Pool.\n     *\n     * If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens\n     * taken from the Pool, which must be greater than or equal to `limit`.\n     *\n     * If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens\n     * sent to the Pool, which must be less than or equal to `limit`.\n     *\n     * Internal Balance usage and the recipient are determined by the `funds` struct.\n     *\n     * Emits a `Swap` event.\n     */\n    function swap(\n        SingleSwap memory singleSwap,\n        FundManagement memory funds,\n        uint256 limit,\n        uint256 deadline\n    ) external payable returns (uint256);\n\n    /**\n     * @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on\n     * the `kind` value.\n     *\n     * `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address).\n     * Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault.\n     *\n     * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be\n     * used to extend swap behavior.\n     */\n    struct SingleSwap {\n        bytes32 poolId;\n        SwapKind kind;\n        address assetIn;\n        address assetOut;\n        uint256 amount;\n        bytes userData;\n    }\n\n    /**\n     * @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the\n     * `recipient` account.\n     *\n     * If the caller is not `sender`, it must be an authorized relayer for them.\n     *\n     * If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20\n     * transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender`\n     * must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of\n     * `joinPool`.\n     *\n     * If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of\n     * transferred. This matches the behavior of `exitPool`.\n     *\n     * Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a\n     * revert.\n     */\n    struct FundManagement {\n        address sender;\n        bool fromInternalBalance;\n        address payable recipient;\n        bool toInternalBalance;\n    }\n\n    function getProtocolFeesCollector() external view returns (IProtocolFeesCollector);\n\n    /**\n     * @dev Simulates a call to `batchSwap`, returning an array of Vault asset deltas. Calls to `swap` cannot be\n     * simulated directly, but an equivalent `batchSwap` call can and will yield the exact same result.\n     *\n     * Each element in the array corresponds to the asset at the same index, and indicates the number of tokens (or ETH)\n     * the Vault would take from the sender (if positive) or send to the recipient (if negative). The arguments it\n     * receives are the same that an equivalent `batchSwap` call would receive.\n     *\n     * Unlike `batchSwap`, this function performs no checks on the sender or recipient field in the `funds` struct.\n     * This makes it suitable to be called by off-chain applications via eth_call without needing to hold tokens,\n     * approve them for the Vault, or even know a user's address.\n     *\n     * Note that this function is not 'view' (due to implementation details): the client code must explicitly execute\n     * eth_call instead of eth_sendTransaction.\n     */\n    function queryBatchSwap(\n        SwapKind kind,\n        BatchSwapStep[] memory swaps,\n        IAsset[] memory assets,\n        FundManagement memory funds\n    ) external returns (int256[] memory assetDeltas);\n}\n"
      },
      "src/interfaces/swapper/ISwapRouter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ISyncSwapper } from \"src/interfaces/swapper/ISyncSwapper.sol\";\n\ninterface ISwapRouter {\n    struct SwapData {\n        address token;\n        address pool;\n        ISyncSwapper swapper;\n        bytes data;\n    }\n\n    error MaxSlippageExceeded();\n    error SwapRouteLookupFailed(address from, address to);\n    error SwapFailed();\n\n    event SwapRouteSet(address indexed token, SwapData[] routes);\n    event SwapForQuoteSuccessful(\n        address indexed assetToken,\n        uint256 sellAmount,\n        address indexed quoteToken,\n        uint256 minBuyAmount,\n        uint256 buyAmount\n    );\n\n    /**\n     * @notice Sets a new swap route for a given asset token.\n     * @param assetToken The asset token for which the swap route is being set.\n     * @param _swapRoute The new swap route as an array of SwapData. The last element represents the quoteToken.\n     * @dev Each 'hop' in the swap route is validated using the respective swapper's validate function. The validate\n     * function ensures that the encoded data contains the correct 'fromAddress' and 'toAddress' (swapData.token), and\n     * verifies that these tokens are in the pool.\n     */\n    function setSwapRoute(address assetToken, SwapData[] calldata _swapRoute) external;\n\n    /**\n     * @notice Swaps the asset token for the quote token.\n     * @dev We're adopting an \"exact in, variable out\" model for all our swaps. This ensures that the entire sellAmount\n     * is used, eliminating the need for additional balance checks and refunds. This model is expected to be followed by\n     * all swapper implementations to maintain consistency and to optimize for gas efficiency.\n     * @param assetToken The address of the asset token to swap.\n     * @param sellAmount The exact amount of the asset token to swap.\n     * @param quoteToken The address of the quote token.\n     * @param minBuyAmount The minimum amount of the quote token expected to be received from the swap.\n     * @return The amount received from the swap.\n     */\n    function swapForQuote(\n        address assetToken,\n        uint256 sellAmount,\n        address quoteToken,\n        uint256 minBuyAmount\n    ) external returns (uint256);\n}\n"
      },
      "src/interfaces/external/balancer/IBasePool.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.17;\n\ninterface IBasePool {\n    /// @notice Returns the pool ID\n    function getPoolId() external view returns (bytes32);\n\n    /// @notice Returns the current swap fee percentage as a 18 decimal fixed point number\n    /// @return The current swap fee percentage\n    function getSwapFeePercentage() external view returns (uint256);\n}\n"
      },
      "src/swapper/adapters/BaseAdapter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\nimport { ISyncSwapper } from \"src/interfaces/swapper/ISyncSwapper.sol\";\n\n/// @dev Reminder from ISyncSwapper: we're adopting an \"exact in, variable out\" model for all our swaps. This ensures\n/// that the entire sellAmount is used, eliminating the need for additional balance checks and refunds. This model is\n/// expected to be followed by all swapper implementations to maintain consistency and to optimize for gas efficiency.\nabstract contract BaseAdapter is ISyncSwapper {\n    ISwapRouter public immutable router;\n\n    constructor(address _router) {\n        Errors.verifyNotZero(_router, \"router\");\n        router = ISwapRouter(_router);\n    }\n\n    /// @dev Reverts if the delegate caller is not the router.\n    modifier onlyRouter() {\n        if (address(this) != address(router)) revert Errors.AccessDenied();\n        _;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    function safeTransfer(\n        IERC20 token,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    function safeIncreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        uint256 newAllowance = token.allowance(address(this), spender) + value;\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            uint256 newAllowance = oldAllowance - value;\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n        }\n    }\n\n    function safePermit(\n        IERC20Permit token,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal {\n        uint256 nonceBefore = token.nonces(owner);\n        token.permit(owner, spender, value, deadline, v, r, s);\n        uint256 nonceAfter = token.nonces(owner);\n        require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        if (returndata.length > 0) {\n            // Return data is optional\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}\n"
      },
      "src/interfaces/external/balancer/IProtocolFeesCollector.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\ninterface IProtocolFeesCollector {\n    function getSwapFeePercentage() external view returns (uint256);\n}\n"
      },
      "src/interfaces/external/balancer/IAsset.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.17;\n\n/**\n * @dev This is an empty interface used to represent either ERC20-conforming token contracts or ETH (using the zero\n * address sentinel value). We're just relying on the fact that `interface` can be used to declare new address-like\n * types.\n *\n * This concept is unrelated to a Pool's Asset Managers.\n */\ninterface IAsset {\n// solhint-disable-previous-line no-empty-blocks\n}\n"
      },
      "src/interfaces/swapper/ISyncSwapper.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\n\ninterface ISyncSwapper {\n    error DataMismatch(string element);\n    error InvalidIndex();\n\n    /**\n     * @notice Swaps sellToken for buyToken\n     * @param pool The address of the pool for the swapper\n     * @param sellTokenAddress The address of the token to sell\n     * @param sellAmount The amount of sellToken to sell\n     * @param buyTokenAddress The address of the token to buy\n     * @param minBuyAmount The minimum amount of buyToken expected\n     * @param data Additional data used differently by the different swappers\n     * @return actualBuyAmount The actual amount received from the swap\n     */\n    function swap(\n        address pool,\n        address sellTokenAddress,\n        uint256 sellAmount,\n        address buyTokenAddress,\n        uint256 minBuyAmount,\n        bytes memory data\n    ) external returns (uint256 actualBuyAmount);\n\n    /**\n     * @notice Validates that the swapData contains the correct information, ensuring that the encoded data contains the\n     * correct 'fromAddress' and 'toAddress' (swapData.token), and verifies that these tokens are in the pool\n     * @dev This function should revert with a DataMismatch error if the swapData is invalid\n     * @param fromAddress The address from which the swap originates\n     * @param swapData The data associated with the swap that needs to be validated\n     */\n    function validate(address fromAddress, ISwapRouter.SwapData memory swapData) external view;\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "forge-std/=lib/forge-std/src/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "src/=src/",
        "test/=test/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
        "erc4626-tests/=lib/erc4626-tests/",
        "prb-math/=lib/prb-math/",
        "crytic/properties/=lib/properties/",
        "ERC4626/=lib/properties/lib/ERC4626/contracts/",
        "properties/=lib/properties/contracts/",
        "solmate/=lib/properties/lib/solmate/src/",
        "usingtellor/=lib/usingtellor/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs"
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "london",
      "viaIR": false,
      "libraries": {
        "src/destinations/adapters/CurveV2FactoryCryptoAdapter.sol": {
          "CurveV2FactoryCryptoAdapter": "0xe46E7634d6e0BC954407078818fF62eDfF414184"
        },
        "src/destinations/adapters/rewards/ConvexRewardsAdapter.sol": {
          "ConvexRewards": "0x08F295fD04B52Ab6a7f8A86bC9A0A1c0EA8aDE01"
        },
        "src/destinations/adapters/staking/ConvexAdapter.sol": {
          "ConvexStaking": "0x6c2f91978004B0A769c622dfe352EdF331044d49"
        },
        "src/strategy/StructuredLinkedList.sol": {
          "StructuredLinkedList": "0x6C566a67b34CFed9821FC7433750A1391fa97989"
        },
        "src/strategy/WithdrawalQueue.sol": {
          "WithdrawalQueue": "0xC754773B0e8CaFbD17e978be3c31ef34869ba733"
        }
      }
    }
  }}