ETH Price: $3,250.98 (+3.49%)
Gas: 4 Gwei

Contract

0x3c11F6265Ddec22f4d049Dde480615735f451646
 
Transaction Hash
Method
Block
From
To
0xe51207cc9258d492a5f79215702c315ed31377b55b012b605f58ae77a33bc3d8 Call(pending)2024-07-26 14:48:1953 mins ago1722005299IN
Mimic: Swapper
0.0001919149 ETH(Pending)(Pending)
0xf5797b0df75bc9d4b648670cd233271c53d370fd9a925421d6642358c6fa48fd Call(pending)2024-07-25 9:37:3830 hrs ago1721900258IN
Mimic: Swapper
0 ETH(Pending)(Pending)
0x8c6d86461dcb0e78eec8f2c879f19d0df2751db12398cc7a268febd13737ea5e Call(pending)2024-07-25 9:37:3130 hrs ago1721900251IN
Mimic: Swapper
0.00005985 ETH(Pending)(Pending)
0x728a687d0956ad9c64f75e08090d457c47b28d77c9368be781414db3bd8738b1 Call(pending)2024-07-25 9:37:3030 hrs ago1721900250IN
Mimic: Swapper
0.000040997 ETH(Pending)(Pending)
0x1a53ffdd60f488fbf2115351ebc90a5a7a47424b69ca67e74db6117eaa30fcfd Call(pending)2024-07-25 8:27:2031 hrs ago1721896040IN
Mimic: Swapper
0.0100232133 ETH(Pending)(Pending)
0xb10634000845db0cac69becad6b4bc70acc615ed5bf480c1bfc59d273b9009b1 Call(pending)2024-07-25 8:08:0631 hrs ago1721894886IN
Mimic: Swapper
0.0001408 ETH(Pending)(Pending)
0x90bb91f547c4be089937eae1c8faeb4e06033307114f8dd7cee044f0d32c6fec Call(pending)2024-07-25 8:02:1631 hrs ago1721894536IN
Mimic: Swapper
0.0160418079 ETH(Pending)(Pending)
0x6fd3f10e481f73e7b8c8d0a70990b9f0a52dba143cc7fc32b860482811c7f4a6 Call(pending)2024-07-25 7:18:5732 hrs ago1721891937IN
Mimic: Swapper
0 ETH(Pending)(Pending)
0xdc31d8d09ca093ea02e4ae1fdb14f7a48f34242a035df5f327ac68d3d5953ca5 Call(pending)2024-07-25 7:07:1932 hrs ago1721891239IN
Mimic: Swapper
0 ETH(Pending)(Pending)
0xf4bbba610b01f9aef6dc857d0327906c69e8f8ff268cac6698d217859eaaf0ce Call(pending)2024-07-25 7:06:1932 hrs ago1721891179IN
Mimic: Swapper
0.2400872044 ETH(Pending)(Pending)
0x8b1edaa1a61d21e3d691cdfe3da3513934ae914386104c9e4edfe5b73f3af903 Call(pending)2024-07-21 7:59:275 days ago1721548767IN
Mimic: Swapper
0.00005 ETH(Pending)(Pending)
0x1cb6d2654c8f002b49da37cc309e77c98fe0e950ae566f78087c86774d09309b Call(pending)2024-07-21 4:48:215 days ago1721537301IN
Mimic: Swapper
0 ETH(Pending)(Pending)
0x00c3d0e9a0d1dac7503974f51c0456acbb40d237b3a25ba478f3ea79faf6c851 Call(pending)2024-07-21 3:57:055 days ago1721534225IN
Mimic: Swapper
0.0037281935 ETH(Pending)(Pending)
0x2fef9d0f4f27a6107d9a3b80f0f87d767473595e15f40f48903e053f538e5a5d Call(pending)2024-07-21 3:44:385 days ago1721533478IN
Mimic: Swapper
0.0015797452 ETH(Pending)(Pending)
0x66814323959294aa7fe58769d6a3c173ef8f3df61dd7cd35f96861f09695e531 Call(pending)2024-07-21 3:43:085 days ago1721533388IN
Mimic: Swapper
0.001 ETH(Pending)(Pending)
0xd875bb7df973f358976c6faaa19bf85e893ad66aa7f05fe0e5abedf26da22765 Call(pending)2024-07-21 3:39:175 days ago1721533157IN
Mimic: Swapper
0 ETH(Pending)(Pending)
0x7fd91a744360e2a5e7a6f50522618de34def26c0631fffc0d7f530e2d5e1ac81 Call(pending)2024-07-21 3:27:305 days ago1721532450IN
Mimic: Swapper
0.0001067013 ETH(Pending)(Pending)
0xe99c7538fa5adf0fa685b2ce09d32fa9a847eecdf8f7adc8c2db66de246982ca Call(pending)2024-07-21 3:01:315 days ago1721530891IN
Mimic: Swapper
0.0017888489 ETH(Pending)(Pending)
0xc52d9ea6f1ab1abceb2d523544db42c3d42a3588a588df8a2f6cdc2abdc0ccff Call(pending)2024-07-21 2:56:135 days ago1721530573IN
Mimic: Swapper
0.0002036839 ETH(Pending)(Pending)
0x24fe9db69c99d50adb66672f10cfca4d517a5fa303265d49f3c995219c507193 Call(pending)2024-07-21 2:45:065 days ago1721529906IN
Mimic: Swapper
0 ETH(Pending)(Pending)
0x39fef955928c295d15a390813f236900486d8db425e7e8ba6e1a4f6833dc525f Call(pending)2024-07-21 2:43:025 days ago1721529782IN
Mimic: Swapper
0.00475 ETH(Pending)(Pending)
0x5950c338de99685114e4ce526850a38422b26c0ef1b8c7d801eb8d18a576acb4 Call(pending)2024-07-21 2:42:435 days ago1721529763IN
Mimic: Swapper
0.000239319 ETH(Pending)(Pending)
0x897704b33512217e851a6daf66fe19d117e91732334a21e58f8443a2653f6ac5 Call(pending)2024-07-21 2:40:145 days ago1721529614IN
Mimic: Swapper
0 ETH(Pending)(Pending)
0x1f9dff98b866d391dfd09deccd43782d791b1acd6a9ddf4c27f7b5ad2929590d Call(pending)2024-07-21 2:38:495 days ago1721529529IN
Mimic: Swapper
0.0000719258 ETH(Pending)(Pending)
0x93a02dba7ed9476c9a21c0af40f0a420436c5855c908c086db375fdbdd5de0f4 Call(pending)2024-07-21 2:28:045 days ago1721528884IN
Mimic: Swapper
0.0001725166 ETH(Pending)(Pending)
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
203916742024-07-26 15:39:232 mins ago1722008363
0x3c11F626...35f451646
0 ETH
203916742024-07-26 15:39:232 mins ago1722008363
0x3c11F626...35f451646
0 ETH
203916742024-07-26 15:39:232 mins ago1722008363
0x3c11F626...35f451646
0 ETH
203916742024-07-26 15:39:232 mins ago1722008363
0x3c11F626...35f451646
0 ETH
203916742024-07-26 15:39:232 mins ago1722008363
0x3c11F626...35f451646
0 ETH
203916742024-07-26 15:39:232 mins ago1722008363
0x3c11F626...35f451646
0 ETH
203916692024-07-26 15:38:233 mins ago1722008303
0x3c11F626...35f451646
0 ETH
203916692024-07-26 15:38:233 mins ago1722008303
0x3c11F626...35f451646
0 ETH
203916692024-07-26 15:38:233 mins ago1722008303
0x3c11F626...35f451646
0 ETH
203916692024-07-26 15:38:233 mins ago1722008303
0x3c11F626...35f451646
0 ETH
203916692024-07-26 15:38:233 mins ago1722008303
0x3c11F626...35f451646
0.013 ETH
203916572024-07-26 15:35:595 mins ago1722008159
0x3c11F626...35f451646
0 ETH
203916572024-07-26 15:35:595 mins ago1722008159
0x3c11F626...35f451646
0 ETH
203916572024-07-26 15:35:595 mins ago1722008159
0x3c11F626...35f451646
0 ETH
203916572024-07-26 15:35:595 mins ago1722008159
0x3c11F626...35f451646
0 ETH
203916572024-07-26 15:35:595 mins ago1722008159
0x3c11F626...35f451646
0.002 ETH
203916542024-07-26 15:35:236 mins ago1722008123
0x3c11F626...35f451646
0 ETH
203916542024-07-26 15:35:236 mins ago1722008123
0x3c11F626...35f451646
0 ETH
203916542024-07-26 15:35:236 mins ago1722008123
0x3c11F626...35f451646
0 ETH
203916542024-07-26 15:35:236 mins ago1722008123
0x3c11F626...35f451646
0 ETH
203916542024-07-26 15:35:236 mins ago1722008123
0x3c11F626...35f451646
0.002 ETH
203916512024-07-26 15:34:476 mins ago1722008087
0x3c11F626...35f451646
0 ETH
203916512024-07-26 15:34:476 mins ago1722008087
0x3c11F626...35f451646
0 ETH
203916512024-07-26 15:34:476 mins ago1722008087
0x3c11F626...35f451646
0 ETH
203916512024-07-26 15:34:476 mins ago1722008087
0x3c11F626...35f451646
0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Swapper

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
File 1 of 50 : Swapper.sol
// 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.8.0;

import { Pausable } from '@openzeppelin/contracts/security/Pausable.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { Address } from '@openzeppelin/contracts/utils/Address.sol';
import { EnumerableSet } from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';

import { Denominations } from '@mimic-fi/v2-helpers/contracts/utils/Denominations.sol';
import { ISmartVault } from '@mimic-fi/v2-smart-vault/contracts/ISmartVault.sol';
import { BaseAction } from '@mimic-fi/v2-smart-vaults-base/contracts/actions/BaseAction.sol';

/**
 * @title Swapper
 * @dev Public swapper action. This action allows any user to leverage a public smart vault to perform arbitray swaps.
 */
contract Swapper is BaseAction, Pausable {
    using Address for address payable;
    using EnumerableSet for EnumerableSet.UintSet;

    // Empty bytes array constant
    bytes private constant NO_DATA = new bytes(0);

    // Enumerable set of allowed sources
    EnumerableSet.UintSet private sources;

    /**
     * @dev Emitted every time a source is allowed or disallowed
     */
    event SourceSet(uint256 indexed source, bool allowed);

    /**
     * @dev Creates a new swapper action
     * @param admin Address that will be granted with admin permissions
     * @param registry Address of the Mimic Registry
     */
    constructor(address admin, address registry) BaseAction(admin, registry) {
        // solhint-disable-previous-line no-empty-blocks
    }

    /**
     * @dev Tells the list of allowed sources
     */
    function getAllowedSources() external view returns (uint256[] memory) {
        return sources.values();
    }

    /**
     * @dev Tells whether a source is allowed or not
     * @param source Number identifying the source being queried
     */
    function isSourceAllowed(uint256 source) public view returns (bool) {
        return sources.contains(source);
    }

    /**
     * @dev Change a source allowance. Sender must be authorized.
     * @param source Number identifying the source to be set
     * @param allowed Whether the source should be allowed or not
     * @return success True if the source was actually added or removed from the list of allowed sources
     */
    function setSource(uint256 source, bool allowed) external auth returns (bool success) {
        require(source <= type(uint8).max, 'SWAPPER_INVALID_SOURCE_ID');
        success = allowed ? sources.add(source) : sources.remove(source);
        if (success) emit SourceSet(source, allowed);
    }

    /**
     * @dev Pause the swap action. Sender must be authorized.
     */
    function pause() external auth {
        _pause();
    }

    /**
     * @dev Unpause the swap action. Sender must be authorized.
     */
    function unpause() external auth {
        _unpause();
    }

    /**
     * @dev Swaps two tokens
     * @param source Source where the swap will be executed.
     * @param tokenIn Token being sent
     * @param tokenOut Token being received
     * @param amountIn Amount of tokenIn being swapped
     * @param minAmountOut Minimum amount of tokenOut expected to be received
     * @param data Extra data that may enable or not different behaviors depending on the source picked
     */
    function call(
        uint8 source,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        bytes memory data
    ) external payable nonReentrant whenNotPaused {
        require(isSourceAllowed(source), 'SWAPPER_SOURCE_NOT_ALLOWED');
        require(tokenIn != tokenOut, 'SWAPPER_SAME_TOKENS');
        require(tokenIn != address(0), 'SWAPPER_TOKEN_IN_ZERO');
        require(tokenOut != address(0), 'SWAPPER_TOKEN_OUT_ZERO');
        require(amountIn > 0, 'SWAPPER_AMOUNT_IN_ZERO');
        require(minAmountOut > 0, 'SWAPPER_MIN_AMOUNT_OUT_ZERO');

        // Final swap amount in is either the wrapped amount in case token in is the native token,
        // or the amount collected by the smart vault in case it is another ERC20 token
        uint256 swapAmountIn;
        if (Denominations.isNativeToken(tokenIn)) {
            require(msg.value == amountIn, 'SWAPPER_UNEXPECTED_VALUE');
            payable(address(smartVault)).sendValue(amountIn);
            swapAmountIn = smartVault.wrap(amountIn, NO_DATA);
        } else {
            require(msg.value == 0, 'SWAPPER_VALUE_GT_ZERO');
            require(IERC20(tokenIn).allowance(msg.sender, address(smartVault)) >= amountIn, 'SWAPPER_BAD_ALLOWANCE');
            swapAmountIn = smartVault.collect(tokenIn, msg.sender, amountIn, NO_DATA);
        }

        // Note that the swap should only be executed if this is not actually a wrap/unwrap only action
        // In that case, the action is already covered by the wrap (above) or unwrap (below)
        uint256 amountOut;
        address swapTokenIn = _wrappedIfNative(tokenIn);
        address swapTokenOut = _wrappedIfNative(tokenOut);
        if (swapTokenIn == swapTokenOut) {
            amountOut = swapAmountIn;
            require(amountOut >= minAmountOut, 'SWAPPER_WRAP_MIN_AMOUNT_OUT');
        } else {
            amountOut = smartVault.swap(
                source,
                swapTokenIn,
                swapTokenOut,
                swapAmountIn,
                ISmartVault.SwapLimit.MinAmountOut,
                minAmountOut,
                data
            );
        }

        // Finally unwrap if necessary and withdraw token out as requested to the sender
        uint256 toWithdraw = Denominations.isNativeToken(tokenOut) ? smartVault.unwrap(amountOut, NO_DATA) : amountOut;
        smartVault.withdraw(tokenOut, toWithdraw, msg.sender, NO_DATA);
        emit Executed();
    }
}

File 2 of 50 : IBridgeConnector.sol
// 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.8.0;

import '@mimic-fi/v2-registry/contracts/implementations/IImplementation.sol';

/**
 * @title IBridgeConnector
 * @dev Bridge Connector interface to bridge tokens between different chains. It must follow IImplementation interface.
 */
interface IBridgeConnector is IImplementation {
    /**
     * @dev Enum identifying the sources proposed: Hop only for now.
     */
    enum Source {
        Hop
    }

    /**
     * @dev Bridge assets to a different chain
     * @param source Source to execute the requested bridge op
     * @param chainId ID of the destination chain
     * @param token Address of the token to be bridged
     * @param amountIn Amount of tokens to be bridged
     * @param minAmountOut Minimum amount of tokens willing to receive on the destination chain
     * @param recipient Address that will receive the tokens on the destination chain
     * @param data ABI encoded data that will depend on the requested source
     */
    function bridge(
        uint8 source,
        uint256 chainId,
        address token,
        uint256 amountIn,
        uint256 minAmountOut,
        address recipient,
        bytes memory data
    ) external;
}

File 3 of 50 : Authorizer.sol
// 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.8.0;

import './IAuthorizer.sol';

/**
 * @title Authorizer
 * @dev Authorization module to be used by contracts that need to implement permissions for their methods.
 * It provides a permissions model to list who is allowed to call what function in a contract. And only accounts
 * authorized to manage those permissions are the ones that are allowed to authorize or unauthorize accounts.
 */
contract Authorizer is IAuthorizer {
    // Constant used to denote that a permission is open to anyone
    address public constant ANY_ADDRESS = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);

    // Internal mapping to tell who is allowed to do what indexed by (account, function selector)
    mapping (address => mapping (bytes4 => bool)) private authorized;

    /**
     * @dev Modifier that should be used to tag protected functions
     */
    modifier auth() {
        _authenticate(msg.sender, msg.sig);
        _;
    }

    /**
     * @dev Tells whether someone is allowed to call a function or not. It returns true if it's allowed to anyone.
     * @param who Address asking permission for
     * @param what Function selector asking permission for
     */
    function isAuthorized(address who, bytes4 what) public view override returns (bool) {
        return authorized[ANY_ADDRESS][what] || authorized[who][what];
    }

    /**
     * @dev Authorizes someone to call a function. Sender must be authorize to do so.
     * @param who Address to be authorized
     * @param what Function selector to be granted
     */
    function authorize(address who, bytes4 what) external override auth {
        _authorize(who, what);
    }

    /**
     * @dev Unauthorizes someone to call a function. Sender must be authorize to do so.
     * @param who Address to be unauthorized
     * @param what Function selector to be revoked
     */
    function unauthorize(address who, bytes4 what) external override auth {
        _unauthorize(who, what);
    }

    /**
     * @dev Internal function to authenticate someone over a function.
     * It reverts if the given account is not authorized to call the requested function.
     * @param who Address to be authenticated
     * @param what Function selector to be authenticated
     */
    function _authenticate(address who, bytes4 what) internal view {
        require(isAuthorized(who, what), 'AUTH_SENDER_NOT_ALLOWED');
    }

    /**
     * @dev Internal function to authorize someone to call a function
     * @param who Address to be authorized
     * @param what Function selector to be granted
     */
    function _authorize(address who, bytes4 what) internal {
        authorized[who][what] = true;
        emit Authorized(who, what);
    }

    /**
     * @dev Internal function to unauthorize someone to call a function
     * @param who Address to be unauthorized
     * @param what Function selector to be revoked
     */
    function _unauthorize(address who, bytes4 what) internal {
        authorized[who][what] = false;
        emit Unauthorized(who, what);
    }
}

File 4 of 50 : IAuthorizer.sol
// 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.8.0;

/**
 * @title IAuthorizer
 */
interface IAuthorizer {
    /**
     * @dev Emitted when an account is authorized to call a function
     */
    event Authorized(address indexed who, bytes4 what);

    /**
     * @dev Emitted when an account is unauthorized to call a function
     */
    event Unauthorized(address indexed who, bytes4 what);

    /**
     * @dev Authorizes someone to call a function. Sender must be authorize to do so.
     * @param who Address to be authorized
     * @param what Function selector to be granted
     */
    function authorize(address who, bytes4 what) external;

    /**
     * @dev Unauthorizes someone to call a function. Sender must be authorize to do so.
     * @param who Address to be unauthorized
     * @param what Function selector to be revoked
     */
    function unauthorize(address who, bytes4 what) external;

    /**
     * @dev Tells whether someone is allowed to call a function or not. It returns true if it's allowed to anyone.
     * @param who Address asking permission for
     * @param what Function selector asking permission for
     */
    function isAuthorized(address who, bytes4 what) external view returns (bool);
}

File 5 of 50 : FixedPoint.sol
// 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.8.0;

/**
 * @title FixedPoint
 * @dev Math library to operate with fixed point values with 18 decimals
 */
library FixedPoint {
    // 1 in fixed point value: 18 decimal places
    uint256 internal constant ONE = 1e18;

    /**
     * @dev Multiplies two fixed point numbers rounding down
     */
    function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            uint256 product = a * b;
            require(a == 0 || product / a == b, 'MUL_OVERFLOW');
            return product / ONE;
        }
    }

    /**
     * @dev Multiplies two fixed point numbers rounding up
     */
    function mulUp(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            uint256 product = a * b;
            require(a == 0 || product / a == b, 'MUL_OVERFLOW');
            return product == 0 ? 0 : (((product - 1) / ONE) + 1);
        }
    }

    /**
     * @dev Divides two fixed point numbers rounding down
     */
    function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            require(b != 0, 'ZERO_DIVISION');
            if (a == 0) return 0;
            uint256 aInflated = a * ONE;
            require(aInflated / a == ONE, 'DIV_INTERNAL');
            return aInflated / b;
        }
    }

    /**
     * @dev Divides two fixed point numbers rounding up
     */
    function divUp(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            require(b != 0, 'ZERO_DIVISION');
            if (a == 0) return 0;
            uint256 aInflated = a * ONE;
            require(aInflated / a == ONE, 'DIV_INTERNAL');
            return ((aInflated - 1) / b) + 1;
        }
    }
}

File 6 of 50 : UncheckedMath.sol
// 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.8.0;

/**
 * @title UncheckedMath
 * @dev Math library to perform unchecked operations
 */
library UncheckedMath {
    /**
     * @dev Unsafely adds two unsigned integers
     */
    function uncheckedAdd(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            return a + b;
        }
    }

    /**
     * @dev Unsafely subtracts two unsigned integers
     */
    function uncheckedSub(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            return a - b;
        }
    }

    /**
     * @dev Unsafely multiplies two unsigned integers
     */
    function uncheckedMul(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            return a * b;
        }
    }

    /**
     * @dev Unsafely multiplies two signed integers
     */
    function uncheckedMul(int256 a, int256 b) internal pure returns (int256) {
        unchecked {
            return a * b;
        }
    }

    /**
     * @dev Unsafely divides two unsigned integers
     */
    function uncheckedDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            return a / b;
        }
    }
}

File 7 of 50 : Denominations.sol
// 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.8.0;

/**
 * @title Denominations
 * @dev Provides a list of ground denominations for those tokens that cannot be represented by an ERC20.
 * For now, the only needed is the native token that could be ETH, MATIC, or other depending on the layer being operated.
 */
library Denominations {
    address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217
    address internal constant USD = address(840);

    function isNativeToken(address token) internal pure returns (bool) {
        return token == NATIVE_TOKEN;
    }
}

File 8 of 50 : ERC20Helpers.sol
// 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.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

import './Denominations.sol';

/**
 * @title ERC20Helpers
 * @dev Provides a list of ERC20 helper methods
 */
library ERC20Helpers {
    function approve(address token, address to, uint256 amount) internal {
        SafeERC20.safeApprove(IERC20(token), to, 0);
        SafeERC20.safeApprove(IERC20(token), to, amount);
    }

    function transfer(address token, address to, uint256 amount) internal {
        if (Denominations.isNativeToken(token)) Address.sendValue(payable(to), amount);
        else SafeERC20.safeTransfer(IERC20(token), to, amount);
    }

    function balanceOf(address token, address account) internal view returns (uint256) {
        if (Denominations.isNativeToken(token)) return address(account).balance;
        else return IERC20(token).balanceOf(address(account));
    }
}

File 9 of 50 : IWrappedNativeToken.sol
// 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.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

/**
 * @title IWrappedNativeToken
 */
interface IWrappedNativeToken is IERC20 {
    /**
     * @dev Wraps msg.value into the wrapped-native token
     */
    function deposit() external payable;

    /**
     * @dev Unwraps requested amount to the native token
     */
    function withdraw(uint256 amount) external;
}

File 10 of 50 : IPriceFeedProvider.sol
// 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.8.0;

/**
 * @title IPriceFeedProvider
 * @dev Contract providing price feed references for (base, quote) token pairs
 */
interface IPriceFeedProvider {
    /**
     * @dev Emitted every time a price feed is set for (base, quote) pair
     */
    event PriceFeedSet(address indexed base, address indexed quote, address feed);

    /**
     * @dev Tells the price feed address for (base, quote) pair. It returns the zero address if there is no one set.
     * @param base Token to be rated
     * @param quote Token used for the price rate
     */
    function getPriceFeed(address base, address quote) external view returns (address);

    /**
     * @dev Sets a of price feed
     * @param base Token base to be set
     * @param quote Token quote to be set
     * @param feed Price feed to be set
     */
    function setPriceFeed(address base, address quote, address feed) external;

    /**
     * @dev Sets a list of price feeds
     * @param bases List of token bases to be set
     * @param quotes List of token quotes to be set
     * @param feeds List of price feeds to be set
     */
    function setPriceFeeds(address[] memory bases, address[] memory quotes, address[] memory feeds) external;
}

File 11 of 50 : PriceFeedProvider.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/math/UncheckedMath.sol';

import './IPriceFeedProvider.sol';

/**
 * @title IPriceFeedProvider
 * @dev Contract providing price feed references for (base, quote) token pairs
 */
contract PriceFeedProvider is IPriceFeedProvider {
    using UncheckedMath for uint256;

    // Mapping of price feeds from "token A" to "token B"
    mapping (address => mapping (address => address)) private _priceFeeds;

    /**
     * @dev Tells the price feed address for (base, quote) pair. It returns the zero address if there is no one set.
     * @param base Token to be rated
     * @param quote Token used for the price rate
     */
    function getPriceFeed(address base, address quote) external view override returns (address) {
        return _priceFeeds[base][quote];
    }

    /**
     * @dev Sets a of price feed
     * @param base Token base to be set
     * @param quote Token quote to be set
     * @param feed Price feed to be set
     */
    function setPriceFeed(address base, address quote, address feed) public virtual override {
        _priceFeeds[base][quote] = feed;
        emit PriceFeedSet(base, quote, feed);
    }

    /**
     * @dev Sets a list of price feeds. Sender must be authorized.
     * @param bases List of token bases to be set
     * @param quotes List of token quotes to be set
     * @param feeds List of price feeds to be set
     */
    function setPriceFeeds(address[] memory bases, address[] memory quotes, address[] memory feeds)
        public
        virtual
        override
    {
        require(bases.length == quotes.length, 'SET_FEEDS_INVALID_QUOTES_LENGTH');
        require(bases.length == feeds.length, 'SET_FEEDS_INVALID_FEEDS_LENGTH');
        for (uint256 i = 0; i < bases.length; i = i.uncheckedAdd(1)) setPriceFeed(bases[i], quotes[i], feeds[i]);
    }
}

File 12 of 50 : IPriceOracle.sol
// 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.8.0;

import '@mimic-fi/v2-registry/contracts/implementations/IImplementation.sol';

/**
 * @title IPriceOracle
 * @dev Oracle that interfaces with external feeds to provide quotes for tokens based on any other token.
 * It must support also `IImplementation`.
 */
interface IPriceOracle is IImplementation {
    /**
     * @dev Tells the price of a token (base) in a given quote. The response is expressed using the corresponding
     * number of decimals so that when performing a fixed point product of it by a `base` amount it results in
     * a value expressed in `quote` decimals. For example, if `base` is ETH and `quote` is USDC, then the returned
     * value is expected to be expressed using 6 decimals:
     *
     * FixedPoint.mul(X[ETH], price[USDC/ETH]) = FixedPoint.mul(X[18], price[6]) = X * price [6]
     *
     * @param provider Contract providing the price feeds to use by the oracle
     * @param base Token to rate
     * @param quote Token used for the price rate
     */
    function getPrice(address provider, address base, address quote) external view returns (uint256);
}

File 13 of 50 : BaseAuthorizedImplementation.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/auth/Authorizer.sol';

import './BaseImplementation.sol';

/**
 * @title BaseAuthorizedImplementation
 * @dev BaseImplementation using the Authorizer mixin. Base implementations that want to use the Authorizer
 * permissions mechanism should inherit from this contract instead.
 */
abstract contract BaseAuthorizedImplementation is BaseImplementation, Authorizer {
    /**
     * @dev Creates a new BaseAuthorizedImplementation
     * @param admin Address to be granted authorize and unauthorize permissions
     * @param registry Address of the Mimic Registry
     */
    constructor(address admin, address registry) BaseImplementation(registry) {
        _authorize(admin, Authorizer.authorize.selector);
        _authorize(admin, Authorizer.unauthorize.selector);
    }
}

File 14 of 50 : BaseImplementation.sol
// 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.8.0;

import '@openzeppelin/contracts/proxy/utils/Initializable.sol';

import './IImplementation.sol';
import '../registry/IRegistry.sol';

/**
 * @title BaseImplementation
 * @dev This implementation contract comes with an immutable reference to an implementations registry where it should
 * be registered as well (checked during initialization). It allows requesting new instances of other registered
 * implementations to as another safety check to make sure valid instances are referenced in case it's needed.
 */
abstract contract BaseImplementation is IImplementation {
    // Immutable implementations registry reference
    address public immutable override registry;

    /**
     * @dev Creates a new BaseImplementation
     * @param _registry Address of the Mimic Registry where dependencies will be validated against
     */
    constructor(address _registry) {
        registry = _registry;
    }

    /**
     * @dev Internal function to validate a new dependency that must be registered as stateless.
     * It checks the new dependency is registered, not deprecated, and stateless.
     * @param dependency New stateless dependency to be set
     */
    function _validateStatelessDependency(address dependency) internal view {
        require(_validateDependency(dependency), 'DEPENDENCY_NOT_STATELESS');
    }

    /**
     * @dev Internal function to validate a new dependency that cannot be registered as stateless.
     * It checks the new dependency is registered, not deprecated, and not stateful.
     * @param dependency New stateful dependency to be set
     */
    function _validateStatefulDependency(address dependency) internal view {
        require(!_validateDependency(dependency), 'DEPENDENCY_NOT_STATEFUL');
    }

    /**
     * @dev Internal function to validate a new dependency. It checks the dependency is registered and not deprecated.
     * @param dependency New dependency to be set
     * @return Whether the dependency is stateless or not
     */
    function _validateDependency(address dependency) private view returns (bool) {
        (bool stateless, bool deprecated, bytes32 namespace) = IRegistry(registry).implementationData(dependency);
        require(namespace != bytes32(0), 'DEPENDENCY_NOT_REGISTERED');
        require(!deprecated, 'DEPENDENCY_DEPRECATED');
        return stateless;
    }
}

File 15 of 50 : IImplementation.sol
// 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.8.0;

// solhint-disable func-name-mixedcase

/**
 * @title IImplementation
 * @dev Implementation interface that must be followed for implementations to be registered in the Mimic Registry
 */
interface IImplementation {
    /**
     * @dev Tells the namespace under which the implementation is registered in the Mimic Registry
     */
    function NAMESPACE() external view returns (bytes32);

    /**
     * @dev Tells the address of the Mimic Registry
     */
    function registry() external view returns (address);
}

File 16 of 50 : InitializableAuthorizedImplementation.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/auth/Authorizer.sol';

import './InitializableImplementation.sol';

/**
 * @title InitializableAuthorizedImplementation
 * @dev InitializableImplementation using the Authorizer mixin. Initializable implementations that want to use the
 * Authorizer permissions mechanism should inherit from this contract instead.
 */
abstract contract InitializableAuthorizedImplementation is InitializableImplementation, Authorizer {
    /**
     * @dev Creates a new InitializableAuthorizedImplementation
     * @param registry Address of the Mimic Registry
     */
    constructor(address registry) InitializableImplementation(registry) {
        // solhint-disable-previous-line no-empty-blocks
    }

    /**
     * @dev Initialization function that authorizes an admin account to authorize and unauthorize accounts.
     * Note this function can only be called from a function marked with the `initializer` modifier.
     * @param admin Address to be granted authorize and unauthorize permissions
     */
    function _initialize(address admin) internal onlyInitializing {
        _initialize();
        _authorize(admin, Authorizer.authorize.selector);
        _authorize(admin, Authorizer.unauthorize.selector);
    }
}

File 17 of 50 : InitializableImplementation.sol
// 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.8.0;

import '@openzeppelin/contracts/proxy/utils/Initializable.sol';

import './BaseImplementation.sol';

/**
 * @title InitializableImplementation
 * @dev Implementation contract to be used through proxies. Inheriting contracts are meant to be initialized through
 * initialization functions instead of constructor functions. It allows re-using the same logic contract while making
 * deployments cheaper.
 */
abstract contract InitializableImplementation is BaseImplementation, Initializable {
    /**
     * @dev Creates a new BaseImplementation. Note that initializers are disabled at creation time.
     */
    constructor(address registry) BaseImplementation(registry) {
        _disableInitializers();
    }

    /**
     * @dev Initialization function.
     * Note this function can only be called from a function marked with the `initializer` modifier.
     */
    function _initialize() internal view onlyInitializing {
        // solhint-disable-previous-line no-empty-blocks
    }
}

File 18 of 50 : IRegistry.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/auth/IAuthorizer.sol';

/**
 * @title IRegistry
 * @dev Registry interface, it must follow the IAuthorizer interface.
 */
interface IRegistry is IAuthorizer {
    /**
     * @dev Emitted every time a new implementation is registered
     */
    event Registered(bytes32 indexed namespace, address indexed implementation, bool stateless);

    /**
     * @dev Emitted every time an implementation is deprecated
     */
    event Deprecated(bytes32 indexed namespace, address indexed implementation);

    /**
     * @dev Tells the data of an implementation:
     * @param implementation Address of the implementation to request it's data
     */
    function implementationData(address implementation)
        external
        view
        returns (bool stateless, bool deprecated, bytes32 namespace);

    /**
     * @dev Tells if a specific implementation is registered under a certain namespace and it's not deprecated
     * @param namespace Namespace asking for
     * @param implementation Address of the implementation to be checked
     */
    function isActive(bytes32 namespace, address implementation) external view returns (bool);

    /**
     * @dev Registers a new implementation for a given namespace
     * @param namespace Namespace to be used for the implementation
     * @param implementation Address of the implementation to be registered
     * @param stateless Whether the implementation is stateless or not
     */
    function register(bytes32 namespace, address implementation, bool stateless) external;

    /**
     * @dev Deprecates a registered implementation
     * @param implementation Address of the implementation to be deprecated
     */
    function deprecate(address implementation) external;
}

File 19 of 50 : BridgeConnectorLib.sol
// 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.8.0;

import '@openzeppelin/contracts/utils/Address.sol';

import '@mimic-fi/v2-bridge-connector/contracts/IBridgeConnector.sol';

/**
 * @title BridgeConnectorLib
 * @dev Library used to delegate-call bridge ops and decode return data correctly
 */
library BridgeConnectorLib {
    /**
     * @dev Delegate-calls a bridge to the bridge connector and decodes de expected data
     * IMPORTANT! This helper method does not check any of the given params, these should be checked beforehand.
     */
    function bridge(
        address connector,
        uint8 source,
        uint256 chainId,
        address token,
        uint256 amountIn,
        uint256 minAmountOut,
        address recipient,
        bytes memory data
    ) internal {
        bytes memory bridgeData = abi.encodeWithSelector(
            IBridgeConnector.bridge.selector,
            source,
            chainId,
            token,
            amountIn,
            minAmountOut,
            recipient,
            data
        );

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = connector.delegatecall(bridgeData);
        Address.verifyCallResult(success, returndata, 'BRIDGE_CALL_REVERTED');
    }
}

File 20 of 50 : StrategyLib.sol
// 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.8.0;

import '@openzeppelin/contracts/utils/Address.sol';

import '@mimic-fi/v2-strategies/contracts/IStrategy.sol';

/**
 * @title StrategyLib
 * @dev Library used to delegate-call to strategy and decode return data correctly
 */
library StrategyLib {
    /**
     * @dev Delegate-calls a claim to a strategy and decodes de expected data
     * IMPORTANT! This helper method does not check any of the given params, these should be checked beforehand.
     */
    function claim(address strategy, bytes memory data) internal returns (address[] memory, uint256[] memory) {
        bytes memory claimData = abi.encodeWithSelector(IStrategy.claim.selector, data);

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = strategy.delegatecall(claimData);
        Address.verifyCallResult(success, returndata, 'CLAIM_CALL_REVERTED');
        return abi.decode(returndata, (address[], uint256[]));
    }

    /**
     * @dev Delegate-calls a join to a strategy and decodes de expected data
     * IMPORTANT! This helper method does not check any of the given params, these should be checked beforehand.
     */
    function join(
        address strategy,
        address[] memory tokensIn,
        uint256[] memory amountsIn,
        uint256 slippage,
        bytes memory data
    ) internal returns (address[] memory tokensOut, uint256[] memory amountsOut, uint256 value) {
        bytes memory joinData = abi.encodeWithSelector(IStrategy.join.selector, tokensIn, amountsIn, slippage, data);

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = strategy.delegatecall(joinData);
        Address.verifyCallResult(success, returndata, 'JOIN_CALL_REVERTED');
        return abi.decode(returndata, (address[], uint256[], uint256));
    }

    /**
     * @dev Delegate-calls a exit to a strategy and decodes de expected data
     * IMPORTANT! This helper method does not check any of the given params, these should be checked beforehand.
     */
    function exit(
        address strategy,
        address[] memory tokensIn,
        uint256[] memory amountsIn,
        uint256 slippage,
        bytes memory data
    ) internal returns (address[] memory tokensOut, uint256[] memory amountsOut, uint256 value) {
        bytes memory exitData = abi.encodeWithSelector(IStrategy.exit.selector, tokensIn, amountsIn, slippage, data);

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = strategy.delegatecall(exitData);
        Address.verifyCallResult(success, returndata, 'EXIT_CALL_REVERTED');
        return abi.decode(returndata, (address[], uint256[], uint256));
    }
}

File 21 of 50 : SwapConnectorLib.sol
// 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.8.0;

import '@openzeppelin/contracts/utils/Address.sol';

import '@mimic-fi/v2-swap-connector/contracts/ISwapConnector.sol';

/**
 * @title SwapConnectorLib
 * @dev Library used to delegate-call swaps and decode return data correctly
 */
library SwapConnectorLib {
    /**
     * @dev Delegate-calls a swap to the swap connector and decodes de expected data
     * IMPORTANT! This helper method does not check any of the given params, these should be checked beforehand.
     */
    function swap(
        address connector,
        uint8 source,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        bytes memory data
    ) internal returns (uint256 amountOut) {
        bytes memory swapData = abi.encodeWithSelector(
            ISwapConnector.swap.selector,
            source,
            tokenIn,
            tokenOut,
            amountIn,
            minAmountOut,
            data
        );

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = connector.delegatecall(swapData);
        Address.verifyCallResult(success, returndata, 'SWAP_CALL_REVERTED');
        return abi.decode(returndata, (uint256));
    }
}

File 22 of 50 : ISmartVault.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/auth/IAuthorizer.sol';
import '@mimic-fi/v2-price-oracle/contracts/feeds/IPriceFeedProvider.sol';
import '@mimic-fi/v2-registry/contracts/implementations/IImplementation.sol';

/**
 * @title ISmartVault
 * @dev Mimic Smart Vault interface to manage assets. It must support also `IImplementation` and `IAuthorizer`
 */
interface ISmartVault is IPriceFeedProvider, IImplementation, IAuthorizer {
    enum SwapLimit {
        Slippage,
        MinAmountOut
    }

    enum BridgeLimit {
        Slippage,
        MinAmountOut
    }

    /**
     * @dev Emitted every time a new strategy is set for the Smart Vault
     */
    event StrategySet(address indexed strategy, bool allowed);

    /**
     * @dev Emitted every time a new price oracle is set for the Smart Vault
     */
    event PriceOracleSet(address indexed priceOracle);

    /**
     * @dev Emitted every time a new swap connector is set for the Smart Vault
     */
    event SwapConnectorSet(address indexed swapConnector);

    /**
     * @dev Emitted every time a new bridge connector is set for the Smart Vault
     */
    event BridgeConnectorSet(address indexed bridgeConnector);

    /**
     * @dev Emitted every time a new fee collector is set
     */
    event FeeCollectorSet(address indexed feeCollector);

    /**
     * @dev Emitted every time the withdraw fee percentage is set
     */
    event WithdrawFeeSet(uint256 pct, uint256 cap, address token, uint256 period);

    /**
     * @dev Emitted every time the performance fee percentage is set
     */
    event PerformanceFeeSet(uint256 pct, uint256 cap, address token, uint256 period);

    /**
     * @dev Emitted every time the swap fee percentage is set
     */
    event SwapFeeSet(uint256 pct, uint256 cap, address token, uint256 period);

    /**
     * @dev Emitted every time the bridge fee percentage is set
     */
    event BridgeFeeSet(uint256 pct, uint256 cap, address token, uint256 period);

    /**
     * @dev Emitted every time `call` is called
     */
    event Call(address indexed target, bytes callData, uint256 value, bytes result, bytes data);

    /**
     * @dev Emitted every time `collect` is called
     */
    event Collect(address indexed token, address indexed from, uint256 collected, bytes data);

    /**
     * @dev Emitted every time `withdraw` is called
     */
    event Withdraw(address indexed token, address indexed recipient, uint256 withdrawn, uint256 fee, bytes data);

    /**
     * @dev Emitted every time `wrap` is called
     */
    event Wrap(uint256 amount, uint256 wrapped, bytes data);

    /**
     * @dev Emitted every time `unwrap` is called
     */
    event Unwrap(uint256 amount, uint256 unwrapped, bytes data);

    /**
     * @dev Emitted every time `claim` is called
     */
    event Claim(address indexed strategy, address[] tokens, uint256[] amounts, bytes data);

    /**
     * @dev Emitted every time `join` is called
     */
    event Join(
        address indexed strategy,
        address[] tokensIn,
        uint256[] amountsIn,
        address[] tokensOut,
        uint256[] amountsOut,
        uint256 value,
        uint256 slippage,
        bytes data
    );

    /**
     * @dev Emitted every time `exit` is called
     */
    event Exit(
        address indexed strategy,
        address[] tokensIn,
        uint256[] amountsIn,
        address[] tokensOut,
        uint256[] amountsOut,
        uint256 value,
        uint256[] fees,
        uint256 slippage,
        bytes data
    );

    /**
     * @dev Emitted every time `swap` is called
     */
    event Swap(
        uint8 indexed source,
        address indexed tokenIn,
        address indexed tokenOut,
        uint256 amountIn,
        uint256 amountOut,
        uint256 minAmountOut,
        uint256 fee,
        bytes data
    );

    /**
     * @dev Emitted every time `bridge` is called
     */
    event Bridge(
        uint8 indexed source,
        uint256 indexed chainId,
        address indexed token,
        uint256 amountIn,
        uint256 minAmountOut,
        uint256 fee,
        address recipient,
        bytes data
    );

    /**
     * @dev Tells a strategy is allowed or not
     * @param strategy Address of the strategy being queried
     */
    function isStrategyAllowed(address strategy) external view returns (bool);

    /**
     * @dev Tells the invested value for a strategy
     * @param strategy Address of the strategy querying the invested value of
     */
    function investedValue(address strategy) external view returns (uint256);

    /**
     * @dev Tells the last value accrued for a strategy. Note this value can be outdated.
     * @param strategy Address of the strategy querying the last value of
     */
    function lastValue(address strategy) external view returns (uint256);

    /**
     * @dev Tells the price oracle associated to a Smart Vault
     */
    function priceOracle() external view returns (address);

    /**
     * @dev Tells the swap connector associated to a Smart Vault
     */
    function swapConnector() external view returns (address);

    /**
     * @dev Tells the bridge connector associated to a Smart Vault
     */
    function bridgeConnector() external view returns (address);

    /**
     * @dev Tells the address where fees will be deposited
     */
    function feeCollector() external view returns (address);

    /**
     * @dev Tells the withdraw fee configuration
     */
    function withdrawFee()
        external
        view
        returns (uint256 pct, uint256 cap, address token, uint256 period, uint256 totalCharged, uint256 nextResetTime);

    /**
     * @dev Tells the performance fee configuration
     */
    function performanceFee()
        external
        view
        returns (uint256 pct, uint256 cap, address token, uint256 period, uint256 totalCharged, uint256 nextResetTime);

    /**
     * @dev Tells the swap fee configuration
     */
    function swapFee()
        external
        view
        returns (uint256 pct, uint256 cap, address token, uint256 period, uint256 totalCharged, uint256 nextResetTime);

    /**
     * @dev Tells the bridge fee configuration
     */
    function bridgeFee()
        external
        view
        returns (uint256 pct, uint256 cap, address token, uint256 period, uint256 totalCharged, uint256 nextResetTime);

    /**
     * @dev Tells the address of the wrapped native token
     */
    function wrappedNativeToken() external view returns (address);

    /**
     * @dev Sets a new strategy as allowed or not for a Smart Vault
     * @param strategy Address of the strategy to be set
     * @param allowed Whether the strategy is allowed or not
     */
    function setStrategy(address strategy, bool allowed) external;

    /**
     * @dev Sets a new price oracle to a Smart Vault
     * @param newPriceOracle Address of the new price oracle to be set
     */
    function setPriceOracle(address newPriceOracle) external;

    /**
     * @dev Sets a new swap connector to a Smart Vault
     * @param newSwapConnector Address of the new swap connector to be set
     */
    function setSwapConnector(address newSwapConnector) external;

    /**
     * @dev Sets a new bridge connector to a Smart Vault
     * @param newBridgeConnector Address of the new bridge connector to be set
     */
    function setBridgeConnector(address newBridgeConnector) external;

    /**
     * @dev Sets a new fee collector
     * @param newFeeCollector Address of the new fee collector to be set
     */
    function setFeeCollector(address newFeeCollector) external;

    /**
     * @dev Sets a new withdraw fee configuration
     * @param pct Withdraw fee percentage to be set
     * @param cap New maximum amount of withdraw fees to be charged per period
     * @param token Address of the token cap to be set
     * @param period New cap period length in seconds for the withdraw fee
     */
    function setWithdrawFee(uint256 pct, uint256 cap, address token, uint256 period) external;

    /**
     * @dev Sets a new performance fee configuration
     * @param pct Performance fee percentage to be set
     * @param cap New maximum amount of performance fees to be charged per period
     * @param token Address of the token cap to be set
     * @param period New cap period length in seconds for the performance fee
     */
    function setPerformanceFee(uint256 pct, uint256 cap, address token, uint256 period) external;

    /**
     * @dev Sets a new swap fee configuration
     * @param pct Swap fee percentage to be set
     * @param cap New maximum amount of swap fees to be charged per period
     * @param token Address of the token cap to be set
     * @param period New cap period length in seconds for the swap fee
     */
    function setSwapFee(uint256 pct, uint256 cap, address token, uint256 period) external;

    /**
     * @dev Sets a new bridge fee configuration
     * @param pct Bridge fee percentage to be set
     * @param cap New maximum amount of bridge fees to be charged per period
     * @param token Address of the token cap to be set
     * @param period New cap period length in seconds for the bridge fee
     */
    function setBridgeFee(uint256 pct, uint256 cap, address token, uint256 period) external;

    /**
     * @dev Tells the price of a token (base) in a given quote
     * @param base Token to rate
     * @param quote Token used for the price rate
     */
    function getPrice(address base, address quote) external view returns (uint256);

    /**
     * @dev Execute an arbitrary call from a Smart Vault
     * @param target Address where the call will be sent
     * @param callData Calldata to be used for the call
     * @param value Value in wei that will be attached to the call
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return result Call response if it was successful, otherwise it reverts
     */
    function call(address target, bytes memory callData, uint256 value, bytes memory data)
        external
        returns (bytes memory result);

    /**
     * @dev Collect tokens from a sender to a Smart Vault
     * @param token Address of the token to be collected
     * @param from Address where the tokens will be transfer from
     * @param amount Amount of tokens to be transferred
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return collected Amount of tokens assigned to the Smart Vault
     */
    function collect(address token, address from, uint256 amount, bytes memory data)
        external
        returns (uint256 collected);

    /**
     * @dev Withdraw tokens to an external account
     * @param token Address of the token to be withdrawn
     * @param amount Amount of tokens to withdraw
     * @param recipient Address where the tokens will be transferred to
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return withdrawn Amount of tokens transferred to the recipient address
     */
    function withdraw(address token, uint256 amount, address recipient, bytes memory data)
        external
        returns (uint256 withdrawn);

    /**
     * @dev Wrap an amount of native tokens to the wrapped ERC20 version of it
     * @param amount Amount of native tokens to be wrapped
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return wrapped Amount of tokens wrapped
     */
    function wrap(uint256 amount, bytes memory data) external returns (uint256 wrapped);

    /**
     * @dev Unwrap an amount of wrapped native tokens
     * @param amount Amount of wrapped native tokens to unwrapped
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return unwrapped Amount of tokens unwrapped
     */
    function unwrap(uint256 amount, bytes memory data) external returns (uint256 unwrapped);

    /**
     * @dev Claim strategy rewards
     * @param strategy Address of the strategy to claim rewards
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return tokens Addresses of the tokens received as rewards
     * @return amounts Amounts of the tokens received as rewards
     */
    function claim(address strategy, bytes memory data)
        external
        returns (address[] memory tokens, uint256[] memory amounts);

    /**
     * @dev Join a strategy with an amount of tokens
     * @param strategy Address of the strategy to join
     * @param tokensIn List of token addresses to join with
     * @param amountsIn List of token amounts to join with
     * @param slippage Slippage that will be used to compute the join
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return tokensOut List of token addresses received after the join
     * @return amountsOut List of token amounts received after the join
     */
    function join(
        address strategy,
        address[] memory tokensIn,
        uint256[] memory amountsIn,
        uint256 slippage,
        bytes memory data
    ) external returns (address[] memory tokensOut, uint256[] memory amountsOut);

    /**
     * @dev Exit a strategy
     * @param strategy Address of the strategy to exit
     * @param tokensIn List of token addresses to exit with
     * @param amountsIn List of token amounts to exit with
     * @param slippage Slippage that will be used to compute the exit
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return tokensOut List of token addresses received after the exit
     * @return amountsOut List of token amounts received after the exit
     */
    function exit(
        address strategy,
        address[] memory tokensIn,
        uint256[] memory amountsIn,
        uint256 slippage,
        bytes memory data
    ) external returns (address[] memory tokensOut, uint256[] memory amountsOut);

    /**
     * @dev Swaps two tokens
     * @param source Source to request the swap. It depends on the Swap Connector attached to a Smart Vault.
     * @param tokenIn Token being sent
     * @param tokenOut Token being received
     * @param amountIn Amount of tokenIn being swapped
     * @param limitType Swap limit to be applied: slippage or min amount out
     * @param limitAmount Amount of the swap limit to be applied depending on limitType
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return amountOut Received amount of tokens out
     */
    function swap(
        uint8 source,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        SwapLimit limitType,
        uint256 limitAmount,
        bytes memory data
    ) external returns (uint256 amountOut);

    /**
     * @dev Bridge assets to another chain
     * @param source Source to request the bridge. It depends on the Bridge Connector attached to a Smart Vault.
     * @param chainId ID of the destination chain
     * @param token Address of the token to be bridged
     * @param amount Amount of tokens to be bridged
     * @param limitType Swap limit to be applied: slippage or min amount out
     * @param limitAmount Amount of the swap limit to be applied depending on limitType
     * @param recipient Address that will receive the tokens on the destination chain
     * @param data Extra data that may enable or not different behaviors depending on the implementation
     * @return bridged Amount requested to be bridged after fees
     */
    function bridge(
        uint8 source,
        uint256 chainId,
        address token,
        uint256 amount,
        BridgeLimit limitType,
        uint256 limitAmount,
        address recipient,
        bytes memory data
    ) external returns (uint256 bridged);
}

File 23 of 50 : ISmartVaultsFactory.sol
// 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.8.0;

import '@mimic-fi/v2-registry/contracts/implementations/IImplementation.sol';

/**
 * @title ISmartVaultsFactory
 * @dev Smart Vaults Factory interface, it must follow the IImplementation interface.
 */
interface ISmartVaultsFactory is IImplementation {
    /**
     * @dev Emitted every time a new Smart Vault instance is created
     */
    event Created(address indexed implementation, address indexed instance, bytes initializeResult);

    /**
     * @dev Tells the implementation associated to a contract instance
     * @param instance Address of the instance to request it's implementation
     */
    function implementationOf(address instance) external view returns (address);

    /**
     * @dev Creates a new Smart Vault pointing to a registered implementation
     * @param salt Salt bytes to derivate the address of the new instance
     * @param implementation Address of the implementation to be instanced
     * @param initializeData Arbitrary data to be sent after deployment
     * @return instance Address of the new instance created
     */
    function create(bytes32 salt, address implementation, bytes memory initializeData) external returns (address);
}

File 24 of 50 : SmartVault.sol
// 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.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import '@openzeppelin/contracts/utils/math/Math.sol';

import '@mimic-fi/v2-bridge-connector/contracts/IBridgeConnector.sol';
import '@mimic-fi/v2-helpers/contracts/math/FixedPoint.sol';
import '@mimic-fi/v2-helpers/contracts/math/UncheckedMath.sol';
import '@mimic-fi/v2-helpers/contracts/utils/Denominations.sol';
import '@mimic-fi/v2-helpers/contracts/utils/IWrappedNativeToken.sol';
import '@mimic-fi/v2-price-oracle/contracts/oracle/IPriceOracle.sol';
import '@mimic-fi/v2-price-oracle/contracts/feeds/PriceFeedProvider.sol';
import '@mimic-fi/v2-strategies/contracts/IStrategy.sol';
import '@mimic-fi/v2-swap-connector/contracts/ISwapConnector.sol';
import '@mimic-fi/v2-registry/contracts/implementations/InitializableAuthorizedImplementation.sol';

import './ISmartVault.sol';
import './helpers/StrategyLib.sol';
import './helpers/SwapConnectorLib.sol';
import './helpers/BridgeConnectorLib.sol';

/**
 * @title Smart Vault
 * @dev Smart Vault contract where funds are being held offering a bunch of primitives to allow users model any
 * type of action to manage them, these are: collector, withdraw, swap, bridge, join, exit, bridge, wrap, and unwrap.
 *
 * It inherits from InitializableAuthorizedImplementation which means it's implementation can be cloned
 * from the Mimic Registry and should be initialized depending on each case.
 */
contract SmartVault is ISmartVault, PriceFeedProvider, InitializableAuthorizedImplementation {
    using SafeERC20 for IERC20;
    using FixedPoint for uint256;
    using UncheckedMath for uint256;
    using StrategyLib for address;
    using SwapConnectorLib for address;
    using BridgeConnectorLib for address;

    // Namespace under which the Smart Vault is registered in the Mimic Registry
    bytes32 public constant override NAMESPACE = keccak256('SMART_VAULT');

    /**
     * @dev Fee configuration parameters
     * @param pct Percentage expressed using 16 decimals (1e18 = 100%)
     * @param cap Maximum amount of fees to be charged per period
     * @param token Address of the token to express the cap amount
     * @param period Period length in seconds
     * @param totalCharged Total amount of fees charged in the current period
     * @param nextResetTime Current cap period end date
     */
    struct Fee {
        uint256 pct;
        uint256 cap;
        address token;
        uint256 period;
        uint256 totalCharged;
        uint256 nextResetTime;
    }

    // Price oracle reference
    address public override priceOracle;

    // Swap connector reference
    address public override swapConnector;

    // Bridge connector reference
    address public override bridgeConnector;

    // List of allowed strategies indexed by strategy address
    mapping (address => bool) public override isStrategyAllowed;

    // List of invested values indexed by strategy address
    mapping (address => uint256) public override investedValue;

    // Fee collector address where fees will be deposited
    address public override feeCollector;

    // Withdraw fee configuration
    Fee public override withdrawFee;

    // Performance fee configuration
    Fee public override performanceFee;

    // Swap fee configuration
    Fee public override swapFee;

    // Bridge fee configuration
    Fee public override bridgeFee;

    // Wrapped native token reference
    address public immutable override wrappedNativeToken;

    /**
     * @dev Creates a new Smart Vault implementation with references that should be shared among all implementations
     * @param _wrappedNativeToken Address of the wrapped native token to be used
     * @param _registry Address of the Mimic Registry to be referenced
     */
    constructor(address _wrappedNativeToken, address _registry) InitializableAuthorizedImplementation(_registry) {
        wrappedNativeToken = _wrappedNativeToken;
    }

    /**
     * @dev Initializes the Smart Vault instance
     * @param admin Address that will be granted with admin rights
     */
    function initialize(address admin) external initializer {
        _initialize(admin);
    }

    /**
     * @dev It allows receiving native token transfers
     */
    receive() external payable {
        // solhint-disable-previous-line no-empty-blocks
    }

    /**
     * @dev Sets a new strategy as allowed or not for a Smart Vault. Sender must be authorized.
     * @param strategy Address of the strategy to be set
     * @param allowed Whether the strategy is allowed or not
     */
    function setStrategy(address strategy, bool allowed) external override auth {
        _setStrategy(strategy, allowed);
    }

    /**
     * @dev Sets a new price oracle to a Smart Vault. Sender must be authorized.
     * @param newPriceOracle Address of the new price oracle to be set
     */
    function setPriceOracle(address newPriceOracle) external override auth {
        _setPriceOracle(newPriceOracle);
    }

    /**
     * @dev Sets a new swap connector to a Smart Vault. Sender must be authorized.
     * @param newSwapConnector Address of the new swap connector to be set
     */
    function setSwapConnector(address newSwapConnector) external override auth {
        _setSwapConnector(newSwapConnector);
    }

    /**
     * @dev Sets a new bridge connector to a Smart Vault. Sender must be authorized.
     * @param newBridgeConnector Address of the new bridge connector to be set
     */
    function setBridgeConnector(address newBridgeConnector) external override auth {
        _setBridgeConnector(newBridgeConnector);
    }

    /**
     * @dev Sets a new fee collector. Sender must be authorized.
     * @param newFeeCollector Address of the new fee collector to be set
     */
    function setFeeCollector(address newFeeCollector) external override auth {
        _setFeeCollector(newFeeCollector);
    }

    /**
     * @dev Sets a new withdraw fee. Sender must be authorized.
     * @param pct Withdraw fee percentage to be set
     * @param cap New maximum amount of withdraw fees to be charged per period
     * @param token Address of the token cap to be set
     * @param period New cap period length in seconds for the withdraw fee
     */
    function setWithdrawFee(uint256 pct, uint256 cap, address token, uint256 period) external override auth {
        _setFeeConfiguration(withdrawFee, pct, cap, token, period);
        emit WithdrawFeeSet(pct, cap, token, period);
    }

    /**
     * @dev Sets a new performance fee. Sender must be authorized.
     * @param pct Performance fee percentage to be set
     * @param cap New maximum amount of performance fees to be charged per period
     * @param token Address of the token cap to be set
     * @param period New cap period length in seconds for the performance fee
     */
    function setPerformanceFee(uint256 pct, uint256 cap, address token, uint256 period) external override auth {
        _setFeeConfiguration(performanceFee, pct, cap, token, period);
        emit PerformanceFeeSet(pct, cap, token, period);
    }

    /**
     * @dev Sets a new swap fee. Sender must be authorized.
     * @param pct New swap fee percentage to be set
     * @param cap New maximum amount of swap fees to be charged per period
     * @param token Address of the token cap to be set
     * @param period New cap period length in seconds for the swap fee
     */
    function setSwapFee(uint256 pct, uint256 cap, address token, uint256 period) external override auth {
        _setFeeConfiguration(swapFee, pct, cap, token, period);
        emit SwapFeeSet(pct, cap, token, period);
    }

    /**
     * @dev Sets a new bridge fee. Sender must be authorized.
     * @param pct New bridge fee percentage to be set
     * @param cap New maximum amount of bridge fees to be charged per period
     * @param token Address of the token cap to be set
     * @param period New cap period length in seconds for the bridge fee
     */
    function setBridgeFee(uint256 pct, uint256 cap, address token, uint256 period) external override auth {
        _setFeeConfiguration(bridgeFee, pct, cap, token, period);
        emit BridgeFeeSet(pct, cap, token, period);
    }

    /**
     * @dev Sets a of price feed
     * @param base Token base to be set
     * @param quote Token quote to be set
     * @param feed Price feed to be set
     */
    function setPriceFeed(address base, address quote, address feed)
        public
        override(IPriceFeedProvider, PriceFeedProvider)
        auth
    {
        super.setPriceFeed(base, quote, feed);
    }

    /**
     * @dev Tells the price of a token (base) in a given quote
     * @param base Token to rate
     * @param quote Token used for the price rate
     */
    function getPrice(address base, address quote) public view override returns (uint256) {
        return IPriceOracle(priceOracle).getPrice(address(this), base, quote);
    }

    /**
     * @dev Tells the last value accrued for a strategy. Note this value can be outdated.
     * @param strategy Address of the strategy querying the last value of
     */
    function lastValue(address strategy) public view override returns (uint256) {
        return IStrategy(strategy).lastValue(address(this));
    }

    /**
     * @dev Execute an arbitrary call from a Smart Vault. Sender must be authorized.
     * @param target Address where the call will be sent
     * @param data Calldata to be used for the call
     * @param value Value in wei that will be attached to the call
     * @return result Call response if it was successful, otherwise it reverts
     */
    function call(address target, bytes memory callData, uint256 value, bytes memory data)
        external
        override
        auth
        returns (bytes memory result)
    {
        result = Address.functionCallWithValue(target, callData, value, 'SMART_VAULT_ARBITRARY_CALL_FAIL');
        emit Call(target, callData, value, result, data);
    }

    /**
     * @dev Collect tokens from an external account to a Smart Vault. Sender must be authorized.
     * @param token Address of the token to be collected
     * @param from Address where the tokens will be transfer from
     * @param amount Amount of tokens to be transferred
     * @param data Extra data only logged
     * @return collected Amount of tokens collected
     */
    function collect(address token, address from, uint256 amount, bytes memory data)
        external
        override
        auth
        returns (uint256 collected)
    {
        require(amount > 0, 'COLLECT_AMOUNT_ZERO');

        uint256 previousBalance = IERC20(token).balanceOf(address(this));
        IERC20(token).safeTransferFrom(from, address(this), amount);
        uint256 currentBalance = IERC20(token).balanceOf(address(this));

        collected = currentBalance - previousBalance;
        emit Collect(token, from, collected, data);
    }

    /**
     * @dev Withdraw tokens to an external account. Sender must be authorized.
     * @param token Address of the token to be withdrawn
     * @param amount Amount of tokens to withdraw
     * @param recipient Address where the tokens will be transferred to
     * @param data Extra data only logged
     * @return withdrawn Amount of tokens transferred to the recipient address
     */
    function withdraw(address token, uint256 amount, address recipient, bytes memory data)
        external
        override
        auth
        returns (uint256 withdrawn)
    {
        require(amount > 0, 'WITHDRAW_AMOUNT_ZERO');
        require(recipient != address(0), 'RECIPIENT_ZERO');

        uint256 withdrawFeeAmount = recipient == feeCollector ? 0 : _payFee(token, amount, withdrawFee);
        withdrawn = amount - withdrawFeeAmount;
        _safeTransfer(token, recipient, withdrawn);
        emit Withdraw(token, recipient, withdrawn, withdrawFeeAmount, data);
    }

    /**
     * @dev Wrap an amount of native tokens to the wrapped ERC20 version of it. Sender must be authorized.
     * @param amount Amount of native tokens to be wrapped
     * @param data Extra data only logged
     * @return wrapped Amount of tokens wrapped
     */
    function wrap(uint256 amount, bytes memory data) external override auth returns (uint256 wrapped) {
        require(amount > 0, 'WRAP_AMOUNT_ZERO');
        require(address(this).balance >= amount, 'WRAP_INSUFFICIENT_AMOUNT');

        IWrappedNativeToken wrappedToken = IWrappedNativeToken(wrappedNativeToken);
        uint256 previousBalance = wrappedToken.balanceOf(address(this));
        wrappedToken.deposit{ value: amount }();
        uint256 currentBalance = wrappedToken.balanceOf(address(this));

        wrapped = currentBalance - previousBalance;
        emit Wrap(amount, wrapped, data);
    }

    /**
     * @dev Unwrap an amount of wrapped native tokens. Sender must be authorized.
     * @param amount Amount of wrapped native tokens to unwrapped
     * @param data Extra data only logged
     * @return unwrapped Amount of tokens unwrapped
     */
    function unwrap(uint256 amount, bytes memory data) external override auth returns (uint256 unwrapped) {
        require(amount > 0, 'UNWRAP_AMOUNT_ZERO');

        uint256 previousBalance = address(this).balance;
        IWrappedNativeToken(wrappedNativeToken).withdraw(amount);
        uint256 currentBalance = address(this).balance;

        unwrapped = currentBalance - previousBalance;
        emit Unwrap(amount, unwrapped, data);
    }

    /**
     * @dev Claim strategy rewards. Sender must be authorized.
     * @param strategy Address of the strategy to claim rewards
     * @param data Extra data passed to the strategy and logged
     * @return tokens Addresses of the tokens received as rewards
     * @return amounts Amounts of the tokens received as rewards
     */
    function claim(address strategy, bytes memory data)
        external
        override
        auth
        returns (address[] memory tokens, uint256[] memory amounts)
    {
        require(isStrategyAllowed[strategy], 'STRATEGY_NOT_ALLOWED');
        (tokens, amounts) = strategy.claim(data);
        emit Claim(strategy, tokens, amounts, data);
    }

    /**
     * @dev Join a strategy with an amount of tokens. Sender must be authorized.
     * @param strategy Address of the strategy to join
     * @param tokensIn List of token addresses to join with
     * @param amountsIn List of token amounts to join with
     * @param slippage Slippage that will be used to compute the join
     * @param data Extra data passed to the strategy and logged
     * @return tokensOut List of token addresses received after the join
     * @return amountsOut List of token amounts received after the join
     */
    function join(
        address strategy,
        address[] memory tokensIn,
        uint256[] memory amountsIn,
        uint256 slippage,
        bytes memory data
    ) external override auth returns (address[] memory tokensOut, uint256[] memory amountsOut) {
        require(isStrategyAllowed[strategy], 'STRATEGY_NOT_ALLOWED');
        require(slippage <= FixedPoint.ONE, 'JOIN_SLIPPAGE_ABOVE_ONE');
        require(tokensIn.length == amountsIn.length, 'JOIN_INPUT_INVALID_LENGTH');

        uint256 value;
        (tokensOut, amountsOut, value) = strategy.join(tokensIn, amountsIn, slippage, data);
        require(tokensOut.length == amountsOut.length, 'JOIN_OUTPUT_INVALID_LENGTH');

        investedValue[strategy] = investedValue[strategy] + value;
        emit Join(strategy, tokensIn, amountsIn, tokensOut, amountsOut, value, slippage, data);
    }

    /**
     * @dev Exit a strategy. Sender must be authorized.
     * @param strategy Address of the strategy to exit
     * @param tokensIn List of token addresses to exit with
     * @param amountsIn List of token amounts to exit with
     * @param slippage Slippage that will be used to compute the exit
     * @param data Extra data passed to the strategy and logged
     * @return tokensOut List of token addresses received after the exit
     * @return amountsOut List of token amounts received after the exit
     */
    function exit(
        address strategy,
        address[] memory tokensIn,
        uint256[] memory amountsIn,
        uint256 slippage,
        bytes memory data
    ) external override auth returns (address[] memory tokensOut, uint256[] memory amountsOut) {
        require(isStrategyAllowed[strategy], 'STRATEGY_NOT_ALLOWED');
        require(investedValue[strategy] > 0, 'EXIT_NO_INVESTED_VALUE');
        require(slippage <= FixedPoint.ONE, 'EXIT_SLIPPAGE_ABOVE_ONE');
        require(tokensIn.length == amountsIn.length, 'EXIT_INPUT_INVALID_LENGTH');

        uint256 value;
        (tokensOut, amountsOut, value) = strategy.exit(tokensIn, amountsIn, slippage, data);
        require(tokensOut.length == amountsOut.length, 'EXIT_OUTPUT_INVALID_LENGTH');
        uint256[] memory performanceFeeAmounts = new uint256[](amountsOut.length);

        // It can rely on the last updated value since we have just exited, no need to compute current value
        uint256 valueBeforeExit = lastValue(strategy) + value;
        if (valueBeforeExit <= investedValue[strategy]) {
            // There were losses, invested value is simply reduced using the exited ratio compared to the value
            // before exit. Invested value is round up to avoid interpreting losses due to rounding errors
            investedValue[strategy] -= investedValue[strategy].mulUp(value).divUp(valueBeforeExit);
        } else {
            // If value gains are greater than the exit value, it means only gains are being withdrawn. In that case
            // the taxable amount is the entire exited amount, otherwise it should be the equivalent gains ratio of it.
            uint256 valueGains = valueBeforeExit.uncheckedSub(investedValue[strategy]);
            bool onlyGains = valueGains >= value;

            // If the exit value is greater than the value gains, the invested value should be reduced by the portion
            // of the invested value being exited. Otherwise, it's still the same, only gains are being withdrawn.
            // No need for checked math as we are checking it manually beforehand
            uint256 decrement = onlyGains ? 0 : value.uncheckedSub(valueGains);
            investedValue[strategy] = investedValue[strategy] - decrement;

            // Compute performance fees per token out
            for (uint256 i = 0; i < tokensOut.length; i = i.uncheckedAdd(1)) {
                address token = tokensOut[i];
                uint256 amount = amountsOut[i];
                uint256 taxableAmount = onlyGains ? amount : ((amount * valueGains) / value);
                uint256 feeAmount = _payFee(token, taxableAmount, performanceFee);
                amountsOut[i] = amount - feeAmount;
                performanceFeeAmounts[i] = feeAmount;
            }
        }

        emit Exit(strategy, tokensIn, amountsIn, tokensOut, amountsOut, value, performanceFeeAmounts, slippage, data);
    }

    /**
     * @dev Swaps two tokens. Sender must be authorized.
     * @param source Source to request the swap: Uniswap V2, Uniswap V3, Balancer V2, or Paraswap V5.
     * @param tokenIn Token being sent
     * @param tokenOut Token being received
     * @param amountIn Amount of tokenIn being swapped
     * @param limitType Swap limit to be applied: slippage or min amount out
     * @param limitAmount Amount of the swap limit to be applied depending on limitType
     * @param data Encoded data to specify different swap parameters depending on the source picked
     * @return amountOut Received amount of tokens out
     */
    function swap(
        uint8 source,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        SwapLimit limitType,
        uint256 limitAmount,
        bytes memory data
    ) external override auth returns (uint256 amountOut) {
        require(tokenIn != tokenOut, 'SWAP_SAME_TOKEN');
        require(swapConnector != address(0), 'SWAP_CONNECTOR_NOT_SET');

        uint256 minAmountOut;
        if (limitType == SwapLimit.MinAmountOut) {
            minAmountOut = limitAmount;
        } else if (limitType == SwapLimit.Slippage) {
            require(limitAmount <= FixedPoint.ONE, 'SWAP_SLIPPAGE_ABOVE_ONE');
            uint256 price = getPrice(tokenIn, tokenOut);
            // No need for checked math as we are checking it manually beforehand
            // Always round up the expected min amount out. Limit amount is slippage.
            minAmountOut = amountIn.mulUp(price).mulUp(FixedPoint.ONE.uncheckedSub(limitAmount));
        } else {
            revert('SWAP_INVALID_LIMIT_TYPE');
        }

        uint256 preBalanceIn = IERC20(tokenIn).balanceOf(address(this));
        uint256 preBalanceOut = IERC20(tokenOut).balanceOf(address(this));
        swapConnector.swap(source, tokenIn, tokenOut, amountIn, minAmountOut, data);

        uint256 postBalanceIn = IERC20(tokenIn).balanceOf(address(this));
        require(postBalanceIn >= preBalanceIn - amountIn, 'SWAP_BAD_TOKEN_IN_BALANCE');

        uint256 amountOutBeforeFees = IERC20(tokenOut).balanceOf(address(this)) - preBalanceOut;
        require(amountOutBeforeFees >= minAmountOut, 'SWAP_MIN_AMOUNT');

        uint256 swapFeeAmount = _payFee(tokenOut, amountOutBeforeFees, swapFee);
        amountOut = amountOutBeforeFees - swapFeeAmount;
        emit Swap(source, tokenIn, tokenOut, amountIn, amountOut, minAmountOut, swapFeeAmount, data);
    }

    /**
     * @dev Bridge assets to another chain
     * @param source Source to request the bridge. It depends on the Bridge Connector attached to a Smart Vault.
     * @param chainId ID of the destination chain
     * @param token Address of the token to be bridged
     * @param amount Amount of tokens to be bridged
     * @param limitType Bridge limit to be applied: slippage or min amount out
     * @param limitAmount Amount of the swap limit to be applied depending on limitType
     * @param recipient Address that will receive the tokens on the destination chain
     * @param data Encoded data to specify different bridge parameters depending on the source picked
     * @return bridged Amount requested to be bridged after fees
     */
    function bridge(
        uint8 source,
        uint256 chainId,
        address token,
        uint256 amount,
        BridgeLimit limitType,
        uint256 limitAmount,
        address recipient,
        bytes memory data
    ) external override auth returns (uint256 bridged) {
        require(block.chainid != chainId, 'BRIDGE_SAME_CHAIN');
        require(recipient != address(0), 'BRIDGE_RECIPIENT_ZERO');
        require(bridgeConnector != address(0), 'BRIDGE_CONNECTOR_NOT_SET');

        uint256 bridgeFeeAmount = _payFee(token, amount, bridgeFee);
        bridged = amount - bridgeFeeAmount;

        uint256 minAmountOut;
        if (limitType == BridgeLimit.MinAmountOut) {
            minAmountOut = limitAmount;
        } else if (limitType == BridgeLimit.Slippage) {
            require(limitAmount <= FixedPoint.ONE, 'BRIDGE_SLIPPAGE_ABOVE_ONE');
            // No need for checked math as we are checking it manually beforehand
            // Always round up the expected min amount out. Limit amount is slippage.
            minAmountOut = bridged.mulUp(FixedPoint.ONE.uncheckedSub(limitAmount));
        } else {
            revert('BRIDGE_INVALID_LIMIT_TYPE');
        }

        uint256 preBalanceIn = IERC20(token).balanceOf(address(this));
        bridgeConnector.bridge(source, chainId, token, bridged, minAmountOut, recipient, data);
        uint256 postBalanceIn = IERC20(token).balanceOf(address(this));
        require(postBalanceIn >= preBalanceIn - bridged, 'BRIDGE_BAD_TOKEN_IN_BALANCE');

        emit Bridge(source, chainId, token, bridged, minAmountOut, bridgeFeeAmount, recipient, data);
    }

    /**
     * @dev Internal function to pay the amount of fees to be charged based on a fee configuration to the fee collector
     * @param token Token being charged
     * @param amount Token amount to be taxed with fees
     * @param fee Fee configuration to be applied
     * @return paidAmount Amount of fees paid to the fee collector
     */
    function _payFee(address token, uint256 amount, Fee storage fee) internal returns (uint256 paidAmount) {
        // Fee amounts are always rounded down
        uint256 feeAmount = amount.mulDown(fee.pct);

        // If cap amount or cap period are not set, charge the entire amount
        if (fee.token == address(0) || fee.cap == 0 || fee.period == 0) {
            _safeTransfer(token, feeCollector, feeAmount);
            return feeAmount;
        }

        // Reset cap totalizator if necessary
        if (block.timestamp >= fee.nextResetTime) {
            fee.totalCharged = 0;
            fee.nextResetTime = block.timestamp + fee.period;
        }

        // Calc fee amount in the fee token used for the cap
        uint256 feeTokenPrice = getPrice(token, fee.token);
        uint256 feeAmountInFeeToken = feeAmount.mulDown(feeTokenPrice);

        // Compute fee amount picking the minimum between the chargeable amount and the remaining part for the cap
        if (fee.totalCharged + feeAmountInFeeToken <= fee.cap) {
            paidAmount = feeAmount;
            fee.totalCharged += feeAmountInFeeToken;
        } else if (fee.totalCharged < fee.cap) {
            paidAmount = (fee.cap.uncheckedSub(fee.totalCharged) * feeAmount) / feeAmountInFeeToken;
            fee.totalCharged = fee.cap;
        } else {
            // This case is when the total charged amount is already greater than the cap amount. It could happen if
            // the cap amounts is decreased or if the cap token is changed. In this case the total charged amount is
            // not updated, and the amount to paid is zero.
            paidAmount = 0;
        }

        // Pay fee amount to the fee collector
        _safeTransfer(token, feeCollector, paidAmount);
    }

    /**
     * @dev Internal method to transfer ERC20 or native tokens from a Smart Vault
     * @param token Address of the ERC20 token to transfer
     * @param to Address transferring the tokens to
     * @param amount Amount of tokens to transfer
     */
    function _safeTransfer(address token, address to, uint256 amount) internal {
        if (amount == 0) return;
        if (Denominations.isNativeToken(token)) Address.sendValue(payable(to), amount);
        else IERC20(token).safeTransfer(to, amount);
    }

    /**
     * @dev Sets a new strategy as allowed or not
     * @param strategy Address of the strategy to be set
     * @param allowed Whether the strategy is allowed or not
     */
    function _setStrategy(address strategy, bool allowed) internal {
        if (allowed) _validateStatelessDependency(strategy);
        isStrategyAllowed[strategy] = allowed;
        emit StrategySet(strategy, allowed);
    }

    /**
     * @dev Sets a new price oracle
     * @param newPriceOracle New price oracle to be set
     */
    function _setPriceOracle(address newPriceOracle) internal {
        _validateStatelessDependency(newPriceOracle);
        priceOracle = newPriceOracle;
        emit PriceOracleSet(newPriceOracle);
    }

    /**
     * @dev Sets a new swap connector
     * @param newSwapConnector New swap connector to be set
     */
    function _setSwapConnector(address newSwapConnector) internal {
        _validateStatelessDependency(newSwapConnector);
        swapConnector = newSwapConnector;
        emit SwapConnectorSet(newSwapConnector);
    }

    /**
     * @dev Sets a new bridge connector
     * @param newBridgeConnector New bridge connector to be set
     */
    function _setBridgeConnector(address newBridgeConnector) internal {
        _validateStatelessDependency(newBridgeConnector);
        bridgeConnector = newBridgeConnector;
        emit BridgeConnectorSet(newBridgeConnector);
    }

    /**
     * @dev Internal method to set the fee collector
     * @param newFeeCollector New fee collector to be set
     */
    function _setFeeCollector(address newFeeCollector) internal {
        require(newFeeCollector != address(0), 'FEE_COLLECTOR_ZERO');
        feeCollector = newFeeCollector;
        emit FeeCollectorSet(newFeeCollector);
    }

    /**
     * @dev Internal method to set a new fee cap configuration
     * @param fee Fee configuration to be updated
     * @param pct Fee percentage to be set
     * @param cap New maximum amount of fees to be charged per period
     * @param token Address of the token cap to be set
     * @param period New cap period length in seconds
     */
    function _setFeeConfiguration(Fee storage fee, uint256 pct, uint256 cap, address token, uint256 period) internal {
        require(pct <= FixedPoint.ONE, 'FEE_PCT_ABOVE_ONE');

        // If there is no fee percentage, there must not be a fee cap
        bool isZeroCap = token == address(0) && cap == 0 && period == 0;
        require(pct != 0 || isZeroCap, 'INVALID_CAP_WITH_FEE_ZERO');

        // If there is a cap, all values must be non-zero
        bool isNonZeroCap = token != address(0) && cap != 0 && period != 0;
        require(isZeroCap || isNonZeroCap, 'INCONSISTENT_CAP_VALUES');

        // Changing the fee percentage does not affect the totalizator at all, it only affects future fee charges
        fee.pct = pct;

        // Changing the fee cap amount does not affect the totalizator, it only applies when changing the for the total
        // charged amount. Note that it can happen that the cap amount is lower than the total charged amount if the
        // cap amount is lowered. However, there shouldn't be any accounting issues with that.
        fee.cap = cap;

        // Changing the cap period only affects the end time of the next period, but not the end date of the current one
        fee.period = period;

        // Therefore, only clean the totalizators if the cap is being removed
        if (isZeroCap) {
            fee.totalCharged = 0;
            fee.nextResetTime = 0;
        } else {
            // If cap values are not zero, set the next reset time if it wasn't set already
            // Otherwise, if the cap token is being changed the total charged amount must be updated accordingly
            if (fee.nextResetTime == 0) {
                fee.nextResetTime = block.timestamp + period;
            } else if (fee.token != token) {
                uint256 newTokenPrice = getPrice(fee.token, token);
                fee.totalCharged = fee.totalCharged.mulDown(newTokenPrice);
            }
        }

        // Finally simply set the new requested token
        fee.token = token;
    }
}

File 25 of 50 : BaseAction.sol
// 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.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';

import '@mimic-fi/v2-smart-vault/contracts/ISmartVault.sol';
import '@mimic-fi/v2-helpers/contracts/auth/Authorizer.sol';
import '@mimic-fi/v2-helpers/contracts/utils/Denominations.sol';
import '@mimic-fi/v2-helpers/contracts/utils/ERC20Helpers.sol';
import '@mimic-fi/v2-registry/contracts/implementations/BaseAuthorizedImplementation.sol';

import './IAction.sol';

/**
 * @title BaseAction
 * @dev Simple action implementation with a Smart Vault reference and using the Authorizer mixin
 */
contract BaseAction is IAction, BaseAuthorizedImplementation, ReentrancyGuard {
    bytes32 public constant override NAMESPACE = keccak256('ACTION');

    // Smart Vault reference
    ISmartVault public override smartVault;

    /**
     * @dev Emitted every time a new smart vault is set
     */
    event SmartVaultSet(address indexed smartVault);

    /**
     * @dev Creates a new BaseAction
     * @param admin Address to be granted authorize and unauthorize permissions
     * @param registry Address of the Mimic Registry
     */
    constructor(address admin, address registry) BaseAuthorizedImplementation(admin, registry) {
        // solhint-disable-previous-line no-empty-blocks
    }

    /**
     * @dev Sets the Smart Vault tied to the Action. Sender must be authorized. It can be set only once.
     * @param newSmartVault Address of the smart vault to be set
     */
    function setSmartVault(address newSmartVault) external auth {
        require(address(smartVault) == address(0), 'SMART_VAULT_ALREADY_SET');
        smartVault = ISmartVault(newSmartVault);
        emit SmartVaultSet(newSmartVault);
    }

    /**
     * @dev Tells the balance of the Smart Vault for a given token
     * @param token Address of the token querying the balance of
     * @notice Denominations.NATIVE_TOKEN_ADDRESS can be used to query the native token balance
     */
    function _balanceOf(address token) internal view returns (uint256) {
        return ERC20Helpers.balanceOf(token, address(smartVault));
    }

    /**
     * @dev Tells the wrapped native token address if the given address is the native token
     * @param token Address of the token to be checked
     */
    function _wrappedIfNative(address token) internal view returns (address) {
        return Denominations.isNativeToken(token) ? smartVault.wrappedNativeToken() : token;
    }

    /**
     * @dev Tells whether the given token is either the native or wrapped native token
     * @param token Address of the token being queried
     */
    function _isWrappedOrNativeToken(address token) internal view returns (bool) {
        return Denominations.isNativeToken(token) || token == smartVault.wrappedNativeToken();
    }
}

File 26 of 50 : IAction.sol
// 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.8.0;

import '@mimic-fi/v2-smart-vault/contracts/ISmartVault.sol';
import '@mimic-fi/v2-helpers/contracts/auth/IAuthorizer.sol';

/**
 * @title IAction
 * @dev Action interface it must follow the IAuthorizer interface
 */
interface IAction is IAuthorizer {
    /**
     * @dev Emitted every time an action is executed
     */
    event Executed();

    /**
     * @dev Tells the address of the Smart Vault tied to it, it cannot be changed
     */
    function smartVault() external view returns (ISmartVault);
}

File 27 of 50 : ReceiverAction.sol
// 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.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/Address.sol';

import './BaseAction.sol';

abstract contract ReceiverAction is BaseAction {
    using SafeERC20 for IERC20;

    receive() external payable {
        // solhint-disable-previous-line no-empty-blocks
    }

    function transferToSmartVault(address token, uint256 amount) external auth {
        _transferToSmartVault(token, amount);
    }

    function _transferToSmartVault(address token, uint256 amount) internal {
        ERC20Helpers.transfer(token, address(smartVault), amount);
    }
}

File 28 of 50 : RelayedAction.sol
// 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.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import '@mimic-fi/v2-helpers/contracts/math/FixedPoint.sol';
import '@mimic-fi/v2-helpers/contracts/utils/Denominations.sol';

import './BaseAction.sol';

/**
 * @title RelayedAction
 * @dev Action that offers a relayed mechanism to allow reimbursing tx costs after execution in any ERC20 token.
 * This type of action at least require having withdraw permissions from the Smart Vault tied to it.
 */
abstract contract RelayedAction is BaseAction {
    using FixedPoint for uint256;

    // Base gas amount charged to cover default amounts
    // solhint-disable-next-line func-name-mixedcase
    function BASE_GAS() external view virtual returns (uint256);

    // Note to be used to mark tx cost payments
    bytes private constant REDEEM_GAS_NOTE = bytes('RELAYER');

    // Internal variable used to allow a better developer experience to reimburse tx gas cost
    uint256 private _initialGas;

    // Gas price limit expressed in the native token, if surpassed it wont relay the transaction
    uint256 public gasPriceLimit;

    // Total transaction cost limit expressed in the native token, if surpassed it wont relay the transaction
    uint256 public txCostLimit;

    // List of allowed relayers indexed by address
    mapping (address => bool) public isRelayer;

    /**
     * @dev Emitted every time the relayers list is changed
     */
    event RelayerSet(address indexed relayer, bool allowed);

    /**
     * @dev Emitted every time the relayer limits are set
     */
    event LimitsSet(uint256 gasPriceLimit, uint256 txCostLimit);

    /**
     * @dev Modifier that can be used to reimburse the gas cost of the tagged function paying in a specific token
     */
    modifier redeemGas(address token) {
        _initRelayedTx();
        _;
        _payRelayedTx(token);
    }

    /**
     * @dev Sets a relayer address. Sender must be authorized.
     * @param relayer Address of the relayer to be set
     * @param allowed Whether it should be allowed or not
     */
    function setRelayer(address relayer, bool allowed) external auth {
        isRelayer[relayer] = allowed;
        emit RelayerSet(relayer, allowed);
    }

    /**
     * @dev Sets the relayer limits. Sender must be authorized.
     * @param _gasPriceLimit New gas price limit to be set
     * @param _txCostLimit New total cost limit to be set
     */
    function setLimits(uint256 _gasPriceLimit, uint256 _txCostLimit) external auth {
        gasPriceLimit = _gasPriceLimit;
        txCostLimit = _txCostLimit;
        emit LimitsSet(_gasPriceLimit, _txCostLimit);
    }

    /**
     * @dev Internal init hook used for relayed txs. It checks tx limit validations only when the sender is a relayer.
     */
    function _initRelayedTx() internal {
        if (!isRelayer[msg.sender]) return;
        _initialGas = gasleft();
        uint256 limit = gasPriceLimit;
        require(limit == 0 || tx.gasprice <= limit, 'GAS_PRICE_ABOVE_LIMIT');
    }

    /**
     * @dev Internal function to pay for a relayed tx. Only when the sender is marked as a relayer.
     * @param token Address of the token to use in order to pay the tx cost
     * @return Amount of tokens paid to reimburse the tx cost
     */
    function _payRelayedTx(address token) internal returns (uint256) {
        (bool success, uint256 price) = _tryGetNativeTokenPriceIn(token);
        if (success) return _payRelayedTx(token, price);
        delete _initialGas;
        return 0;
    }

    /**
     * @dev Internal after call hook where tx cost is reimbursed. Only when the sender is marked as a relayer.
     * @param token Address of the token to use in order to pay the tx cost
     * @param price Price of the native token expressed in the given token quote
     * @return Amount of tokens paid to reimburse the tx cost
     */
    function _payRelayedTx(address token, uint256 price) internal returns (uint256) {
        if (!isRelayer[msg.sender]) return 0;
        require(_initialGas > 0, 'RELAYED_TX_NOT_INITIALIZED');

        uint256 limit = txCostLimit;
        uint256 totalGas = _initialGas - gasleft();
        uint256 totalCostNative = (totalGas + RelayedAction(this).BASE_GAS()) * tx.gasprice;
        require(limit == 0 || totalCostNative <= limit, 'TX_COST_ABOVE_LIMIT');

        // Total cost is rounded down to make sure we always match at least the threshold
        uint256 totalCostToken = totalCostNative.mulDown(price);
        smartVault.withdraw(token, totalCostToken, smartVault.feeCollector(), REDEEM_GAS_NOTE);

        delete _initialGas;
        return totalCostToken;
    }

    /**
     * @dev Tries getting the price of the native token quoted in a another token
     * @param token Address of the token to quote the native token in
     * @return success Whether the price query to the smart vault succeeded or not
     * @return price The price fetched or zero if the query didn't succeed
     */
    function _tryGetNativeTokenPriceIn(address token) internal view virtual returns (bool success, uint256 price) {
        if (_isWrappedOrNativeToken(token)) return (true, FixedPoint.ONE);
        try smartVault.getPrice(smartVault.wrappedNativeToken(), token) returns (uint256 result) {
            return (true, result);
        } catch {
            return (false, 0);
        }
    }
}

File 29 of 50 : TimeLockedAction.sol
// 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.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import './BaseAction.sol';

/**
 * @title Time-locked action
 * @dev Action that offers a time-lock mechanism to allow executing it only once during a set period of time
 */
abstract contract TimeLockedAction is BaseAction {
    // Period in seconds
    uint256 public period;

    // Next timestamp in the future when the action can be executed again
    uint256 public nextResetTime;

    /**
     * @dev Emitted every time a time-lock is set
     */
    event TimeLockSet(uint256 period);

    /**
     * @dev Creates a new time-locked action
     */
    constructor() {
        nextResetTime = block.timestamp;
    }

    /**
     * @dev Sets a new period for the time-locked action
     * @param newPeriod New period to be set
     */
    function setTimeLock(uint256 newPeriod) external auth {
        period = newPeriod;
        emit TimeLockSet(newPeriod);
    }

    /**
     * @dev Internal function to tell whether the current time-lock has passed
     */
    function _passesTimeLock() internal view returns (bool) {
        return block.timestamp >= nextResetTime;
    }

    /**
     * @dev Internal function to validate the time-locked action
     */
    function _validateTimeLock() internal {
        require(_passesTimeLock(), 'TIME_LOCK_NOT_EXPIRED');
        nextResetTime = block.timestamp + period;
    }
}

File 30 of 50 : TokenThresholdAction.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/math/FixedPoint.sol';

import './BaseAction.sol';

/**
 * @title TokenThresholdAction
 * @dev Action that offers a token threshold limit. It can be used for minimum swap amounts, or minimum withdrawal
 * amounts, etc. This type of action does not require any specific permission on the Smart Vault.
 */
abstract contract TokenThresholdAction is BaseAction {
    using FixedPoint for uint256;

    address public thresholdToken;
    uint256 public thresholdAmount;

    event ThresholdSet(address indexed token, uint256 amount);

    /**
     * @dev Sets a new threshold configuration. Sender must be authorized.
     * @param token New token threshold to be set
     * @param amount New amount threshold to be set
     */
    function setThreshold(address token, uint256 amount) external auth {
        thresholdToken = token;
        thresholdAmount = amount;
        emit ThresholdSet(token, amount);
    }

    /**
     * @dev Internal function to check the set threshold
     * @param token Token address of the given amount to evaluate the threshold
     * @param amount Amount of tokens to validate the threshold
     */
    function _passesThreshold(address token, uint256 amount) internal view returns (bool) {
        uint256 price = smartVault.getPrice(_wrappedIfNative(token), thresholdToken);
        return _passesThreshold(amount, price);
    }

    /**
     * @dev Internal function to check the set threshold
     * @param amount Amount of tokens to validate the threshold
     * @param price Price of the given token amount quoted in the threshold token
     */
    function _passesThreshold(uint256 amount, uint256 price) internal view returns (bool) {
        // Result balance is rounded down to make sure we always match at least the threshold
        return amount.mulDown(price) >= thresholdAmount;
    }

    /**
     * @dev Internal function to validate the set threshold
     * @param token Token address of the given amount to evaluate the threshold
     * @param amount Amount of tokens to validate the threshold
     */
    function _validateThreshold(address token, uint256 amount) internal view {
        require(_passesThreshold(token, amount), 'MIN_THRESHOLD_NOT_MET');
    }

    /**
     * @dev Internal function to validate the set threshold
     * @param amount Amount of tokens to validate the threshold
     * @param price Price of the given token amount quoted in the threshold token
     */
    function _validateThreshold(uint256 amount, uint256 price) internal view {
        require(_passesThreshold(amount, price), 'MIN_THRESHOLD_NOT_MET');
    }
}

File 31 of 50 : WithdrawalAction.sol
// 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.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import './BaseAction.sol';

/**
 * @title Withdrawal action
 * @dev Action that offers a recipient address where funds can be withdrawn. This type of action at least require
 * having withdraw permissions from the Smart Vault tied to it.
 */
abstract contract WithdrawalAction is BaseAction {
    // Address where tokens will be transferred to
    address public recipient;

    /**
     * @dev Emitted every time the recipient is set
     */
    event RecipientSet(address indexed recipient);

    /**
     * @dev Sets the recipient address. Sender must be authorized.
     * @param newRecipient Address of the new recipient to be set
     */
    function setRecipient(address newRecipient) external auth {
        require(newRecipient != address(0), 'RECIPIENT_ZERO');
        recipient = newRecipient;
        emit RecipientSet(newRecipient);
    }

    /**
     * @dev Internal function to withdraw all the available balance of a token from the Smart Vault to the recipient
     * @param token Address of the token to be withdrawn
     */
    function _withdraw(address token) internal {
        uint256 balance = _balanceOf(token);
        _withdraw(token, balance);
    }

    /**
     * @dev Internal function to withdraw a specific amount of a token from the Smart Vault to the recipient
     * @param token Address of the token to be withdrawn
     * @param amount Amount of tokens to be withdrawn
     */
    function _withdraw(address token, uint256 amount) internal {
        smartVault.withdraw(token, amount, recipient, new bytes(0));
    }
}

File 32 of 50 : Deployer.sol
// 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.8.0;

import '@mimic-fi/v2-smart-vault/contracts/SmartVault.sol';
import '@mimic-fi/v2-smart-vault/contracts/ISmartVaultsFactory.sol';
import '@mimic-fi/v2-helpers/contracts/auth/IAuthorizer.sol';
import '@mimic-fi/v2-helpers/contracts/math/UncheckedMath.sol';
import '@mimic-fi/v2-registry/contracts/registry/IRegistry.sol';

import '../actions/ReceiverAction.sol';
import '../actions/RelayedAction.sol';
import '../actions/TimeLockedAction.sol';
import '../actions/TokenThresholdAction.sol';
import '../actions/WithdrawalAction.sol';
import '../permissions/PermissionsManager.sol';
import '../permissions/PermissionsHelpers.sol';

/**
 * @title Deployer
 * @dev Deployer library offering a bunch of set-up methods to deploy and customize smart vaults
 */
library Deployer {
    using UncheckedMath for uint256;
    using PermissionsHelpers for PermissionsManager;

    // Namespace to use by this deployer to fetch ISmartVaultFactory implementations from the Mimic Registry
    bytes32 private constant SMART_VAULT_FACTORY_NAMESPACE = keccak256('SMART_VAULTS_FACTORY');

    // Namespace to use by this deployer to fetch ISmartVault implementations from the Mimic Registry
    bytes32 private constant SMART_VAULT_NAMESPACE = keccak256('SMART_VAULT');

    // Namespace to use by this deployer to fetch IStrategy implementations from the Mimic Registry
    bytes32 private constant STRATEGY_NAMESPACE = keccak256('STRATEGY');

    // Namespace to use by this deployer to fetch IPriceOracle implementations from the Mimic Registry
    bytes32 private constant PRICE_ORACLE_NAMESPACE = keccak256('PRICE_ORACLE');

    // Namespace to use by this deployer to fetch ISwapConnector implementations from the Mimic Registry
    bytes32 private constant SWAP_CONNECTOR_NAMESPACE = keccak256('SWAP_CONNECTOR');

    // Namespace to use by this deployer to fetch IBridgeConnector implementations from the Mimic Registry
    bytes32 private constant BRIDGE_CONNECTOR_NAMESPACE = keccak256('BRIDGE_CONNECTOR');

    /**
     * @dev Smart vault params
     * @param factory Address of the factory that will be used to deploy an instance of the Smart Vault implementation
     * @param impl Address of the Smart Vault implementation to be used
     * @param salt Salt bytes to derivate the address of the new Smart Vault instance
     * @param admin Address that will be granted with admin rights for the deployed Smart Vault
     * @param bridgeConnector Optional Bridge Connector to set for the Smart Vault
     * @param swapConnector Optional Swap Connector to set for the Smart Vault
     * @param strategies List of strategies to be allowed for the Smart Vault
     * @param priceOracle Optional Price Oracle to set for the Smart Vault
     * @param priceFeedParams List of price feeds to be set for the Smart Vault
     * @param feeCollector Address to be set as the fee collector
     * @param swapFee Swap fee params
     * @param bridgeFee Bridge fee params
     * @param withdrawFee Withdraw fee params
     * @param performanceFee Performance fee params
     */
    struct SmartVaultParams {
        address factory;
        address impl;
        bytes32 salt;
        address admin;
        address[] strategies;
        address bridgeConnector;
        address swapConnector;
        address priceOracle;
        PriceFeedParams[] priceFeedParams;
        address feeCollector;
        address feeCollectorAdmin;
        SmartVaultFeeParams swapFee;
        SmartVaultFeeParams bridgeFee;
        SmartVaultFeeParams withdrawFee;
        SmartVaultFeeParams performanceFee;
    }

    /**
     * @dev Smart Vault price feed params
     * @param base Base token of the price feed
     * @param quote Quote token of the price feed
     * @param feed Address of the price feed
     */
    struct PriceFeedParams {
        address base;
        address quote;
        address feed;
    }

    /**
     * @dev Smart Vault fee configuration parameters
     * @param pct Percentage expressed using 16 decimals (1e18 = 100%)
     * @param cap Maximum amount of fees to be charged per period
     * @param token Address of the token to express the cap amount
     * @param period Period length in seconds
     */
    struct SmartVaultFeeParams {
        uint256 pct;
        uint256 cap;
        address token;
        uint256 period;
    }

    /**
     * @dev Relayed action params
     * @param relayers List of addresses to be marked as allowed executors and in particular as authorized relayers
     * @param gasPriceLimit Gas price limit to be used for the relayed action
     * @param txCostLimit Total transaction cost limit to be used for the relayed action
     */
    struct RelayedActionParams {
        address[] relayers;
        uint256 gasPriceLimit;
        uint256 txCostLimit;
    }

    /**
     * @dev Token threshold action params
     * @param token Address of the token of the threshold
     * @param amount Amount of tokens of the threshold
     */
    struct TokenThresholdActionParams {
        address token;
        uint256 amount;
    }

    /**
     * @dev Time-locked action params
     * @param period Period in seconds to be set for the time lock
     */
    struct TimeLockedActionParams {
        uint256 period;
    }

    /**
     * @dev Withdrawal action params
     * @param recipient Address that will receive the funds from the withdraw action
     */
    struct WithdrawalActionParams {
        address recipient;
    }

    /**
     * @dev Transfer Permission Manager control to a list of owners
     * @param manager Permissions manager that will control the entire Smart Vault and its actions
     * @param owners Addresses that will be able to call the permission manager
     */
    function transferPermissionManagerControl(PermissionsManager manager, address[] memory owners) external {
        manager.authorize(manager, owners, manager.execute.selector);
        manager.unauthorize(manager, address(this), manager.execute.selector);
    }

    /**
     * @dev Create a new Smart Vault instance
     * @param registry Address of the registry to validate the Smart Vault implementation
     * @param manager Permissions manager that will control the entire Smart Vault and its actions
     * @param params Params to customize the Smart Vault to be deployed
     * creating the Smart Vault. Sometimes this is not desired if further customization might take in place.
     */
    function createSmartVault(IRegistry registry, PermissionsManager manager, SmartVaultParams memory params)
        external
        returns (SmartVault smartVault)
    {
        require(params.admin != address(0), 'SMART_VAULT_ADMIN_ZERO');
        require(params.feeCollectorAdmin != address(0), 'SMART_VAULT_FEE_ADMIN_ZERO');

        // Clone requested Smart Vault implementation and initialize
        require(registry.isActive(SMART_VAULT_FACTORY_NAMESPACE, params.factory), 'BAD_SMART_VAULT_FACTORY_IMPL');
        ISmartVaultsFactory factory = ISmartVaultsFactory(params.factory);

        bytes memory initializeData = abi.encodeWithSelector(SmartVault.initialize.selector, address(manager));
        bytes32 senderSalt = keccak256(abi.encodePacked(msg.sender, params.salt));
        smartVault = SmartVault(payable(factory.create(senderSalt, params.impl, initializeData)));

        // Authorize admin to perform any action except from fee collector setter
        bytes4[] memory whats = new bytes4[](19);
        whats[0] = smartVault.collect.selector;
        whats[1] = smartVault.withdraw.selector;
        whats[2] = smartVault.wrap.selector;
        whats[3] = smartVault.unwrap.selector;
        whats[4] = smartVault.claim.selector;
        whats[5] = smartVault.join.selector;
        whats[6] = smartVault.exit.selector;
        whats[7] = smartVault.swap.selector;
        whats[8] = smartVault.bridge.selector;
        whats[9] = smartVault.setStrategy.selector;
        whats[10] = smartVault.setPriceFeed.selector;
        whats[11] = smartVault.setPriceFeeds.selector;
        whats[12] = smartVault.setPriceOracle.selector;
        whats[13] = smartVault.setSwapConnector.selector;
        whats[14] = smartVault.setBridgeConnector.selector;
        whats[15] = smartVault.setWithdrawFee.selector;
        whats[16] = smartVault.setPerformanceFee.selector;
        whats[17] = smartVault.setSwapFee.selector;
        whats[18] = smartVault.setBridgeFee.selector;
        manager.authorize(smartVault, params.admin, whats);

        // Set price feeds if any
        if (params.priceFeedParams.length > 0) {
            manager.authorize(smartVault, address(this), smartVault.setPriceFeed.selector);
            for (uint256 i = 0; i < params.priceFeedParams.length; i = i.uncheckedAdd(1)) {
                PriceFeedParams memory feedParams = params.priceFeedParams[i];
                smartVault.setPriceFeed(feedParams.base, feedParams.quote, feedParams.feed);
            }
            manager.unauthorize(smartVault, address(this), smartVault.setPriceFeed.selector);
        }

        // Set price oracle if given
        if (params.priceOracle != address(0)) {
            require(registry.isActive(PRICE_ORACLE_NAMESPACE, params.priceOracle), 'BAD_PRICE_ORACLE_DEPENDENCY');
            manager.authorize(smartVault, address(this), smartVault.setPriceOracle.selector);
            smartVault.setPriceOracle(params.priceOracle);
            manager.unauthorize(smartVault, address(this), smartVault.setPriceOracle.selector);
        }

        // Set strategies if any
        if (params.strategies.length > 0) {
            manager.authorize(smartVault, address(this), smartVault.setStrategy.selector);
            for (uint256 i = 0; i < params.strategies.length; i = i.uncheckedAdd(1)) {
                require(registry.isActive(STRATEGY_NAMESPACE, params.strategies[i]), 'BAD_STRATEGY_DEPENDENCY');
                smartVault.setStrategy(params.strategies[i], true);
            }
            manager.unauthorize(smartVault, address(this), smartVault.setStrategy.selector);
        }

        // Set swap connector if given
        if (params.swapConnector != address(0)) {
            require(registry.isActive(SWAP_CONNECTOR_NAMESPACE, params.swapConnector), 'BAD_SWAP_CONNECTOR_DEPENDENCY');
            manager.authorize(smartVault, address(this), smartVault.setSwapConnector.selector);
            smartVault.setSwapConnector(params.swapConnector);
            manager.unauthorize(smartVault, address(this), smartVault.setSwapConnector.selector);
        }

        // Set bridge connector if given
        if (params.bridgeConnector != address(0)) {
            bool isActive = registry.isActive(BRIDGE_CONNECTOR_NAMESPACE, params.bridgeConnector);
            require(isActive, 'BAD_BRIDGE_CONNECTOR_DEPENDENCY');
            manager.authorize(smartVault, address(this), smartVault.setBridgeConnector.selector);
            smartVault.setBridgeConnector(params.bridgeConnector);
            manager.unauthorize(smartVault, address(this), smartVault.setBridgeConnector.selector);
        }

        // If no fee collector is given, make sure no fee amounts are requested too
        manager.authorize(smartVault, params.feeCollectorAdmin, smartVault.setFeeCollector.selector);
        if (params.feeCollector != address(0)) {
            manager.authorize(smartVault, address(this), smartVault.setFeeCollector.selector);
            smartVault.setFeeCollector(params.feeCollector);
            manager.unauthorize(smartVault, address(this), smartVault.setFeeCollector.selector);
        } else {
            bool noFees = params.withdrawFee.pct == 0 &&
                params.swapFee.pct == 0 &&
                params.bridgeFee.pct == 0 &&
                params.performanceFee.pct == 0;
            require(noFees, 'SMART_VAULT_FEES_NO_COLLECTOR');
        }

        // Set withdraw fee if not zero
        SmartVaultFeeParams memory withdrawFee = params.withdrawFee;
        if (withdrawFee.pct != 0) {
            manager.authorize(smartVault, address(this), smartVault.setWithdrawFee.selector);
            smartVault.setWithdrawFee(withdrawFee.pct, withdrawFee.cap, withdrawFee.token, withdrawFee.period);
            manager.unauthorize(smartVault, address(this), smartVault.setWithdrawFee.selector);
        }

        // Set swap fee if not zero
        SmartVaultFeeParams memory swapFee = params.swapFee;
        if (swapFee.pct != 0) {
            manager.authorize(smartVault, address(this), smartVault.setSwapFee.selector);
            smartVault.setSwapFee(swapFee.pct, swapFee.cap, swapFee.token, swapFee.period);
            manager.unauthorize(smartVault, address(this), smartVault.setSwapFee.selector);
        }

        // Set bridge fee if not zero
        SmartVaultFeeParams memory bridgeFee = params.bridgeFee;
        if (bridgeFee.pct != 0) {
            manager.authorize(smartVault, address(this), smartVault.setBridgeFee.selector);
            smartVault.setBridgeFee(bridgeFee.pct, bridgeFee.cap, bridgeFee.token, bridgeFee.period);
            manager.unauthorize(smartVault, address(this), smartVault.setBridgeFee.selector);
        }

        // Set performance fee if not zero
        SmartVaultFeeParams memory perfFee = params.performanceFee;
        if (perfFee.pct != 0) {
            manager.authorize(smartVault, address(this), smartVault.setPerformanceFee.selector);
            smartVault.setPerformanceFee(perfFee.pct, perfFee.cap, perfFee.token, perfFee.period);
            manager.unauthorize(smartVault, address(this), smartVault.setPerformanceFee.selector);
        }
    }

    /**
     * @dev Set up a base action
     * @param action Base action to be set up
     * @param manager Permissions manager that will control the entire Smart Vault and its actions
     * @param admin Address that will be granted with admin rights for the Base Action
     * @param smartVault Address of the Smart Vault to be set in the Base Action
     */
    function setupBaseAction(BaseAction action, PermissionsManager manager, address admin, address smartVault)
        external
    {
        require(admin != address(0), 'BASE_ACTION_ADMIN_ZERO');

        manager.authorize(action, Arrays.from(admin, address(this)), action.setSmartVault.selector);
        action.setSmartVault(smartVault);
        manager.unauthorize(action, address(this), action.setSmartVault.selector);
    }

    /**
     * @dev Set up a list of executors for a given action
     * @param action Action whose executors are being allowed
     * @param manager Permissions manager that will control the entire Smart Vault and its actions
     * @param executors List of addresses to be allowed to call the given action
     * @param callSelector Selector of the function to allow the list of executors
     */
    function setupActionExecutors(
        BaseAction action,
        PermissionsManager manager,
        address[] memory executors,
        bytes4 callSelector
    ) external {
        manager.authorize(action, executors, callSelector);
    }

    /**
     * @dev Set up a Relayed action
     * @param action Relayed action to be configured
     * @param manager Permissions manager that will control the entire Smart Vault and its actions
     * @param admin Address that will be granted with admin rights for the Relayed action
     * @param params Params to customize the Relayed action
     */
    function setupRelayedAction(
        RelayedAction action,
        PermissionsManager manager,
        address admin,
        RelayedActionParams memory params
    ) external {
        // Authorize admin to set relayers and txs limits
        require(admin != address(0), 'RELAYED_ACTION_ADMIN_ZERO');

        address[] memory whos = Arrays.from(admin, address(this));
        bytes4[] memory whats = Arrays.from(action.setLimits.selector, action.setRelayer.selector);

        manager.authorize(action, whos, whats);

        action.setLimits(params.gasPriceLimit, params.txCostLimit);
        for (uint256 i = 0; i < params.relayers.length; i = i.uncheckedAdd(1)) {
            action.setRelayer(params.relayers[i], true);
        }

        manager.unauthorize(action, address(this), whats);
    }

    /**
     * @dev Set up a Token Threshold action
     * @param action Token threshold action to be configured
     * @param manager Permissions manager that will control the entire Smart Vault and its actions
     * @param admin Address that will be granted with admin rights for the Token Threshold action
     * @param params Params to customize the Token Threshold action
     */
    function setupTokenThresholdAction(
        TokenThresholdAction action,
        PermissionsManager manager,
        address admin,
        TokenThresholdActionParams memory params
    ) external {
        require(admin != address(0), 'TOKEN_THRESHOLD_ADMIN_ZERO');
        manager.authorize(action, Arrays.from(admin, address(this)), action.setThreshold.selector);
        action.setThreshold(params.token, params.amount);
        manager.unauthorize(action, address(this), action.setThreshold.selector);
    }

    /**
     * @dev Set up a Time-locked action
     * @param action Time-locked action to be configured
     * @param manager Permissions manager that will control the entire Smart Vault and its actions
     * @param admin Address that will be granted with admin rights for the Time-locked action
     * @param params Params to customize the Time-locked action
     */
    function setupTimeLockedAction(
        TimeLockedAction action,
        PermissionsManager manager,
        address admin,
        TimeLockedActionParams memory params
    ) external {
        require(admin != address(0), 'TIME_LOCKED_ACTION_ADMIN_ZERO');
        manager.authorize(action, Arrays.from(admin, address(this)), action.setTimeLock.selector);
        action.setTimeLock(params.period);
        manager.unauthorize(action, address(this), action.setTimeLock.selector);
    }

    /**
     * @dev Set up a Withdrawal action
     * @param action Relayed action to be configured
     * @param manager Permissions manager that will control the entire Smart Vault and its actions
     * @param admin Address that will be granted with admin rights for the Withdrawal action
     * @param params Params to customize the Withdrawal action
     */
    function setupWithdrawalAction(
        WithdrawalAction action,
        PermissionsManager manager,
        address admin,
        WithdrawalActionParams memory params
    ) external {
        require(admin != address(0), 'WITHDRAWAL_ACTION_ADMIN_ZERO');
        manager.authorize(action, Arrays.from(admin, address(this)), action.setRecipient.selector);
        action.setRecipient(params.recipient);
        manager.unauthorize(action, address(this), action.setRecipient.selector);
    }

    /**
     * @dev Set up a Receiver action
     * @param action Relayed action to be configured
     * @param manager Permissions manager that will control the entire Smart Vault and its actions
     * @param admin Address that will be granted with admin rights for the Receiver action
     */
    function setupReceiverAction(ReceiverAction action, PermissionsManager manager, address admin) external {
        require(admin != address(0), 'RECEIVER_ACTION_ADMIN_ZERO');
        manager.authorize(action, admin, action.transferToSmartVault.selector);
    }
}

File 33 of 50 : Arrays.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/math/UncheckedMath.sol';

/**
 * @title Arrays
 * @dev Helper methods to operate arrays
 */
library Arrays {
    using UncheckedMath for uint256;

    /**
     * @dev Builds an array of addresses based on the given ones
     */
    function concat(address[] memory a, address[] memory b) internal pure returns (address[] memory r) {
        // No need for checked math since we are simply adding two memory array's length
        r = new address[](a.length.uncheckedAdd(b.length));

        // No need for checked math since we are using it to compute indexes manually, always within boundaries
        for (uint256 i = 0; i < a.length; i = i.uncheckedAdd(1)) {
            r[i] = a[i];
        }

        // No need for checked math since we are using it to compute indexes manually, always within boundaries
        for (uint256 i = 0; i < b.length; i = i.uncheckedAdd(1)) {
            r[a.length.uncheckedAdd(i)] = b[i];
        }
    }

    /**
     * @dev Builds an array of addresses based on the given ones
     */
    function from(address a, address[] memory b, address[] memory c) internal pure returns (address[] memory result) {
        // No need for checked math since we are simply adding two memory array's length
        result = new address[](b.length.uncheckedAdd(c.length).uncheckedAdd(1));
        result[0] = a;

        // No need for checked math since we are using it to compute indexes manually, always within boundaries
        for (uint256 i = 0; i < b.length; i = i.uncheckedAdd(1)) {
            result[i.uncheckedAdd(1)] = b[i];
        }

        // No need for checked math since we are using it to compute indexes manually, always within boundaries
        for (uint256 i = 0; i < c.length; i = i.uncheckedAdd(1)) {
            result[b.length.uncheckedAdd(1).uncheckedAdd(i)] = c[i];
        }
    }

    // Address helpers

    function from(address a) internal pure returns (address[] memory r) {
        r = new address[](1);
        r[0] = a;
    }

    function from(address a, address b) internal pure returns (address[] memory r) {
        r = new address[](2);
        r[0] = a;
        r[1] = b;
    }

    function from(address a, address b, address c) internal pure returns (address[] memory r) {
        r = new address[](3);
        r[0] = a;
        r[1] = b;
        r[2] = c;
    }

    function from(address a, address b, address c, address d) internal pure returns (address[] memory r) {
        r = new address[](4);
        r[0] = a;
        r[1] = b;
        r[2] = c;
        r[3] = d;
    }

    function from(address a, address b, address c, address d, address e) internal pure returns (address[] memory r) {
        r = new address[](5);
        r[0] = a;
        r[1] = b;
        r[2] = c;
        r[3] = d;
        r[4] = e;
    }

    // Bytes4 helpers

    function from(bytes4 a) internal pure returns (bytes4[] memory r) {
        r = new bytes4[](1);
        r[0] = a;
    }

    function from(bytes4 a, bytes4 b) internal pure returns (bytes4[] memory r) {
        r = new bytes4[](2);
        r[0] = a;
        r[1] = b;
    }

    function from(bytes4 a, bytes4 b, bytes4 c) internal pure returns (bytes4[] memory r) {
        r = new bytes4[](3);
        r[0] = a;
        r[1] = b;
        r[2] = c;
    }

    function from(bytes4 a, bytes4 b, bytes4 c, bytes4 d) internal pure returns (bytes4[] memory r) {
        r = new bytes4[](4);
        r[0] = a;
        r[1] = b;
        r[2] = c;
        r[3] = d;
    }

    function from(bytes4 a, bytes4 b, bytes4 c, bytes4 d, bytes4 e) internal pure returns (bytes4[] memory r) {
        r = new bytes4[](5);
        r[0] = a;
        r[1] = b;
        r[2] = c;
        r[3] = d;
        r[4] = e;
    }
}

File 34 of 50 : PermissionsData.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/auth/IAuthorizer.sol';

/**
 * @dev Permission
 * @param what Function selector to be referred
 * @param who Address to be referred
 */
struct Permission {
    bytes4 what;
    address who;
}

/**
 * @dev Permission change
 * @param grant Whether the permission should be granted (authorize) or revoked (unauthorize)
 * @param permissions Permission to be changed
 */
struct PermissionChange {
    bool grant;
    Permission permission;
}

/**
 * @dev Permission change request
 * @param target Address of the contract to be affected
 * @param changes List of permission changes to be performed
 */
struct PermissionChangeRequest {
    IAuthorizer target;
    PermissionChange[] changes;
}

File 35 of 50 : PermissionsHelpers.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/auth/IAuthorizer.sol';

import './Arrays.sol';
import './PermissionsManager.sol';
import { Permission, PermissionChange, PermissionChangeRequest } from './PermissionsData.sol';

library PermissionsHelpers {
    /**
     * @dev Builds a permission object
     * @param who Address to be referred
     * @param what Function selector to be referred
     */
    function permission(address who, bytes4 what) internal pure returns (Permission memory) {
        return Permission(what, who);
    }

    /**
     * @dev Builds a permission change object
     * @param grant Whether the permission should be granted or revoked
     * @param who Address to be referred
     * @param what Function selector to be referred
     */
    function change(bool grant, address who, bytes4 what) internal pure returns (PermissionChange memory) {
        return PermissionChange(grant, permission(who, what));
    }

    /**
     * @dev Grants permission to `who` to perform `what` in `where` through the permissions manager `self`
     * @param self Permissions manager to be used
     * @param where Address of the contract where the permission will be granted
     * @param who Address of the account that will be authorized
     * @param what Function selector to be authorized
     */
    function authorize(PermissionsManager self, IAuthorizer where, address who, bytes4 what) internal {
        authorize(self, where, Arrays.from(who), Arrays.from(what));
    }

    /**
     * @dev Revokes permission from `who` to perform `what` in `where` through the permissions manager `self`
     * @param self Permissions manager to be used
     * @param where Address of the contract where the permission will be revoked
     * @param who Address of the account that will be unauthorized
     * @param what Function selector to be unauthorized
     */
    function unauthorize(PermissionsManager self, IAuthorizer where, address who, bytes4 what) internal {
        unauthorize(self, where, Arrays.from(who), Arrays.from(what));
    }

    /**
     * @dev Grants permission to `whos` to perform `what` in `where` through the permissions manager `self`
     * @param self Permissions manager to be used
     * @param where Address of the contract where the permission will be granted
     * @param whos List of addresses of the accounts that will be authorized
     * @param what Function selector to be authorized
     */
    function authorize(PermissionsManager self, IAuthorizer where, address[] memory whos, bytes4 what) internal {
        authorize(self, where, whos, Arrays.from(what));
    }

    /**
     * @dev Revokes permission from `whos` to perform `what` in `where` through the permissions manager `self`
     * @param self Permissions manager to be used
     * @param where Address of the contract where the permission will be revoked
     * @param whos List of addresses of the accounts that will be unauthorized
     * @param what Function selector to be unauthorized
     */
    function unauthorize(PermissionsManager self, IAuthorizer where, address[] memory whos, bytes4 what) internal {
        unauthorize(self, where, whos, Arrays.from(what));
    }

    /**
     * @dev Grants permissions to `who` to perform `whats` in `where` through the permissions manager `self`
     * @param self Permissions manager to be used
     * @param where Address of the contract where the permission will be granted
     * @param who Address of the account that will be authorized
     * @param whats List of function selectors to be authorized
     */
    function authorize(PermissionsManager self, IAuthorizer where, address who, bytes4[] memory whats) internal {
        authorize(self, where, Arrays.from(who), whats);
    }

    /**
     * @dev Revokes permissions from `who` to perform `whats` in `where` through the permissions manager `self`
     * @param self Permissions manager to be used
     * @param where Address of the contract where the permission will be revoked
     * @param who Address of the account that will be unauthorized
     * @param whats List of function selectors to be unauthorized
     */
    function unauthorize(PermissionsManager self, IAuthorizer where, address who, bytes4[] memory whats) internal {
        unauthorize(self, where, Arrays.from(who), whats);
    }

    /**
     * @dev Grants permissions to `whos` to perform `whats` in `where` through the permissions manager `self`
     * @param self Permissions manager to be used
     * @param where Address of the contract where the permission will be granted
     * @param whos List of addresses of the accounts that will be authorized
     * @param whats List of function selectors to be authorized
     */
    function authorize(PermissionsManager self, IAuthorizer where, address[] memory whos, bytes4[] memory whats)
        internal
    {
        execute(self, where, whos, whats, true);
    }

    /**
     * @dev Revokes permissions from `whos` to perform `whats` in `where` through the permissions manager `self`
     * @param self Permissions manager to be used
     * @param where Address of the contract where the permission will be revoked
     * @param whos List of addresses of the accounts that will be unauthorized
     * @param whats List of function selectors to be unauthorized
     */
    function unauthorize(PermissionsManager self, IAuthorizer where, address[] memory whos, bytes4[] memory whats)
        internal
    {
        execute(self, where, whos, whats, false);
    }

    /**
     * @dev Executes a list of permission changes
     * @param self Permissions manager to be used
     * @param where Address of the contract where the permission change will be executed
     * @param whos List of addresses of the accounts that will be affected
     * @param whats List of function selectors to be affected
     * @param grant Whether the permissions should be granted or revoked
     */
    function execute(
        PermissionsManager self,
        IAuthorizer where,
        address[] memory whos,
        bytes4[] memory whats,
        bool grant
    ) private {
        PermissionChangeRequest[] memory requests = new PermissionChangeRequest[](1);
        requests[0].target = where;
        requests[0].changes = new PermissionChange[](whos.length * whats.length);

        for (uint256 i = 0; i < whos.length; i++) {
            for (uint256 j = 0; j < whats.length; j++) {
                uint256 index = (i * whats.length) + j;
                requests[0].changes[index] = change(grant, whos[i], whats[j]);
            }
        }

        self.execute(requests);
    }
}

File 36 of 50 : PermissionsManager.sol
// 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.8.0;

import '@mimic-fi/v2-helpers/contracts/auth/Authorizer.sol';
import '@mimic-fi/v2-helpers/contracts/auth/IAuthorizer.sol';

import { Permission, PermissionChange, PermissionChangeRequest } from './PermissionsData.sol';

/**
 * @title PermissionsManager
 * @dev This implementation is meant to be used as a proxy in order to control many `IAuthorizer` implementations.
 * It allows to implement a layout of permissions over a group of `IAuthorizer` implementations, otherwise in order
 * to connect a big number of `IAuthorizer` implementations between each other, admins would had to perform many
 * transactions manually.
 */
contract PermissionsManager is Authorizer {
    /**
     * @dev Creates a new permission manager, allowing itself to authorize and unauthorize its own permissions
     * @param admin Address that will be allowed to execute permissions changes through the permissions manager
     */
    constructor(address admin) {
        _authorize(admin, PermissionsManager.execute.selector);
        _authorize(admin, PermissionsManager.execute.selector);
        _authorize(address(this), Authorizer.authorize.selector);
        _authorize(address(this), Authorizer.unauthorize.selector);
    }

    /**
     * @dev Executes a list of permissions change requests. Sender must be authorized.
     * @param requests List of requests to be executed
     */
    function execute(PermissionChangeRequest[] memory requests) external auth {
        for (uint256 i = 0; i < requests.length; i++) _execute(requests[i]);
    }

    /**
     * @dev Executes a single permissions change request
     * @param request Request to be executed
     */
    function _execute(PermissionChangeRequest memory request) private {
        IAuthorizer target = request.target;
        for (uint256 i = 0; i < request.changes.length; i++) {
            PermissionChange memory change = request.changes[i];
            (change.grant ? target.authorize : target.unauthorize)(change.permission.who, change.permission.what);
        }
    }
}

File 37 of 50 : IStrategy.sol
// 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.8.0;

import '@mimic-fi/v2-registry/contracts/implementations/IImplementation.sol';

/**
 * @title IStrategy
 * @dev Strategy interface required by Mimic Smart Vaults. It must follow the IImplementation interface.
 */
interface IStrategy is IImplementation {
    /**
     * @dev Tokens accepted to join the strategy
     */
    function joinTokens() external view returns (address[] memory);

    /**
     * @dev Tokens accepted to exit the strategy
     */
    function exitTokens() external view returns (address[] memory);

    /**
     * @dev Tells how much a value unit means expressed in the asset token.
     * For example, if a strategy has a value of 100 in T0, and then it has a value of 120 in T1,
     * and the value rate is 1.5, it means the strategy has earned 30 strategy tokens between T0 and T1.
     */
    function valueRate() external view returns (uint256);

    /**
     * @dev Tells the last value an account has over time. Note this value can be outdated: there could be rewards to
     * be claimed that will affect the accrued value. For example, if an account has a value of 100 in T0, and then it
     * has a value of 120 in T1, it means it gained a 20% between T0 and T1.
     * @param account Address of the account querying the last value of
     */
    function lastValue(address account) external view returns (uint256);

    /**
     * @dev Claim any existing rewards
     * @param data Arbitrary extra data
     * @return tokens Addresses of the tokens received as rewards
     * @return amounts Amounts of the tokens received as rewards
     */
    function claim(bytes memory data) external returns (address[] memory tokens, uint256[] memory amounts);

    /**
     * @dev Join the interfaced DeFi protocol
     * @param tokensIn List of token addresses to join with
     * @param amountsIn List of token amounts to join with
     * @param slippage Slippage value to join with
     * @param data Arbitrary extra data
     * @return tokensOut List of token addresses received after the join
     * @return amountsOut List of token amounts received after the join
     * @return value Value represented by the joined amount
     */
    function join(address[] memory tokensIn, uint256[] memory amountsIn, uint256 slippage, bytes memory data)
        external
        returns (address[] memory tokensOut, uint256[] memory amountsOut, uint256 value);

    /**
     * @dev Exit the interfaced DeFi protocol
     * @param tokensIn List of token addresses to exit with
     * @param amountsIn List of token amounts to exit with
     * @param slippage Slippage value to exit with
     * @param data Arbitrary extra data
     * @return tokensOut List of token addresses received after the exit
     * @return amountsOut List of token amounts received after the exit
     * @return value Value represented by the exited amount
     */
    function exit(address[] memory tokensIn, uint256[] memory amountsIn, uint256 slippage, bytes memory data)
        external
        returns (address[] memory tokensOut, uint256[] memory amountsOut, uint256 value);
}

File 38 of 50 : ISwapConnector.sol
// 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.8.0;

import '@mimic-fi/v2-registry/contracts/implementations/IImplementation.sol';

/**
 * @title ISwapConnector
 * @dev Swap Connector interface to perform token swaps. It must follow the IImplementation interface.
 */
interface ISwapConnector is IImplementation {
    /**
     * @dev Enum identifying the sources proposed: Uniswap V2, Uniswap V3, Balancer V2, Paraswap V5, 1inch V5, and Hop.
     */
    enum Source {
        UniswapV2,
        UniswapV3,
        BalancerV2,
        ParaswapV5,
        OneInchV5,
        Hop
    }

    /**
     * @dev Swaps two tokens
     * @param source Source to execute the requested swap
     * @param tokenIn Token being sent
     * @param tokenOut Token being received
     * @param amountIn Amount of tokenIn being swapped
     * @param minAmountOut Minimum amount of tokenOut willing to receive
     * @param data Encoded data to specify different swap parameters depending on the source picked
     */
    function swap(
        uint8 source,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        bytes memory data
    ) external returns (uint256 amountOut);
}

File 39 of 50 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

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

File 40 of 50 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/Address.sol";

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

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

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

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
     * initialization step. This is essential to configure modules that are added through upgrades and that require
     * initialization.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }
}

File 41 of 50 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

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

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

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

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

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

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 42 of 50 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 43 of 50 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 44 of 50 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 45 of 50 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 46 of 50 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 47 of 50 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 48 of 50 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
        // good first aproximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 1;
        }

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        uint256 result = sqrt(a);
        if (rounding == Rounding.Up && result * result < a) {
            result += 1;
        }
        return result;
    }
}

File 49 of 50 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 50 of 50 : SmartVaultDeployer.sol
// 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.8.0;

import { Ownable } from '@openzeppelin/contracts/access/Ownable.sol';

import { UncheckedMath } from '@mimic-fi/v2-helpers/contracts/math/UncheckedMath.sol';
import { IRegistry } from '@mimic-fi/v2-registry/contracts/registry/IRegistry.sol';
import { SmartVault } from '@mimic-fi/v2-smart-vault/contracts/SmartVault.sol';
import { Deployer } from '@mimic-fi/v2-smart-vaults-base/contracts/deploy/Deployer.sol';
import { Arrays } from '@mimic-fi/v2-smart-vaults-base/contracts/permissions/Arrays.sol';
import { PermissionsHelpers } from '@mimic-fi/v2-smart-vaults-base/contracts/permissions/PermissionsHelpers.sol';
import { PermissionsManager } from '@mimic-fi/v2-smart-vaults-base/contracts/permissions/PermissionsManager.sol';

import { Swapper } from './Swapper.sol';

contract SmartVaultDeployer is Ownable {
    using PermissionsHelpers for PermissionsManager;

    struct Params {
        address[] owners;
        IRegistry registry;
        PermissionsManager manager;
        SwapperActionParams swapperActionParams;
        Deployer.SmartVaultParams smartVaultParams;
    }

    struct SwapperActionParams {
        address impl;
        address admin;
        uint8[] sources;
    }

    constructor(address owner) {
        _transferOwnership(owner);
    }

    function deploy(Params memory params) external {
        SmartVault smartVault = Deployer.createSmartVault(params.registry, params.manager, params.smartVaultParams);
        _setupSwapperAction(smartVault, params.manager, params.swapperActionParams);
        Deployer.transferPermissionManagerControl(params.manager, params.owners);
    }

    function _setupSwapperAction(SmartVault smartVault, PermissionsManager manager, SwapperActionParams memory params)
        internal
    {
        // Create and setup action
        Swapper swapper = Swapper(params.impl);
        Deployer.setupBaseAction(swapper, manager, params.admin, address(smartVault));

        // Set up allowed sources
        manager.authorize(swapper, Arrays.from(params.admin, address(this)), swapper.setSource.selector);
        for (uint256 i = 0; i < params.sources.length; i++) swapper.setSource(params.sources[i], true);
        manager.unauthorize(swapper, address(this), swapper.setSource.selector);

        // Set up pause permissions
        manager.authorize(swapper, params.admin, Arrays.from(swapper.pause.selector, swapper.unpause.selector));

        // Authorize action to collect, swap, wrap, unwrap, and withdraw
        manager.authorize(
            smartVault,
            address(swapper),
            Arrays.from(
                smartVault.collect.selector,
                smartVault.swap.selector,
                smartVault.wrap.selector,
                smartVault.unwrap.selector,
                smartVault.withdraw.selector
            )
        );
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"registry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"bytes4","name":"what","type":"bytes4"}],"name":"Authorized","type":"event"},{"anonymous":false,"inputs":[],"name":"Executed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"smartVault","type":"address"}],"name":"SmartVaultSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"source","type":"uint256"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"SourceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"bytes4","name":"what","type":"bytes4"}],"name":"Unauthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"ANY_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAMESPACE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"},{"internalType":"bytes4","name":"what","type":"bytes4"}],"name":"authorize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"source","type":"uint8"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"call","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getAllowedSources","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"},{"internalType":"bytes4","name":"what","type":"bytes4"}],"name":"isAuthorized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"source","type":"uint256"}],"name":"isSourceAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newSmartVault","type":"address"}],"name":"setSmartVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"source","type":"uint256"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setSource","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"smartVault","outputs":[{"internalType":"contract ISmartVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"},{"internalType":"bytes4","name":"what","type":"bytes4"}],"name":"unauthorize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b5060405162001d9c38038062001d9c833981016040819052620000349162000112565b6001600160a01b0381166080528181818162000058826324cfc3cf60e21b62000089565b6200006b82633323521b60e11b62000089565b50506001805550506002805460ff60a01b19169055506200014a9050565b6001600160a01b0382166000818152602081815260408083206001600160e01b0319861680855290835292819020805460ff19166001179055519182527f7f06c1c34ad47198873da9dcde2e40904035d41b23da3a9132df674a75022df7910160405180910390a25050565b80516001600160a01b03811681146200010d57600080fd5b919050565b600080604083850312156200012657600080fd5b6200013183620000f5565b91506200014160208401620000f5565b90509250929050565b608051611c366200016660003960006102880152611c366000f3fe6080604052600436106100e85760003560e01c80635c975abb1161008a578063933f0f3c11610059578063933f0f3c146102bf578063b771ed9f146102df578063d9972b96146102ff578063f157fac21461031f57600080fd5b80635c975abb1461021a5780636646a436146102565780637b103999146102765780638456cb59146102aa57600080fd5b80633f81a192116100c65780633f81a1921461013757806344ba1fca14610189578063452d98c7146101cb5780634fd49efd146101ed57600080fd5b8063049639fb146100ed57806313e3b57c146101025780633f4ba83a14610122575b600080fd5b6101006100fb36600461176d565b61033f565b005b34801561010e57600080fd5b5061010061011d36600461188c565b610c38565b34801561012e57600080fd5b50610100610d3b565b34801561014357600080fd5b5061015f73ffffffffffffffffffffffffffffffffffffffff81565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561019557600080fd5b506101bd7ff66b632ae3b479ef109dee46eb319414289a8426a08b64d81aad232941f9d22a81565b604051908152602001610180565b3480156101d757600080fd5b506101e0610d73565b60405161018091906118a9565b3480156101f957600080fd5b5060025461015f9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561022657600080fd5b5060025474010000000000000000000000000000000000000000900460ff165b6040519015158152602001610180565b34801561026257600080fd5b506101006102713660046118ed565b610d84565b34801561028257600080fd5b5061015f7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102b657600080fd5b50610100610dc0565b3480156102cb57600080fd5b506101006102da3660046118ed565b610df6565b3480156102eb57600080fd5b506102466102fa36600461194b565b610e2e565b34801561030b57600080fd5b5061024661031a3660046118ed565b610f1a565b34801561032b57600080fd5b5061024661033a366004611975565b610fd0565b6002600154036103965760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60026001556103a3610fdd565b6103af8660ff16610fd0565b6103fb5760405162461bcd60e51b815260206004820152601a60248201527f535741505045525f534f555243455f4e4f545f414c4c4f574544000000000000604482015260640161038d565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16036104765760405162461bcd60e51b815260206004820152601360248201527f535741505045525f53414d455f544f4b454e5300000000000000000000000000604482015260640161038d565b73ffffffffffffffffffffffffffffffffffffffff85166104d95760405162461bcd60e51b815260206004820152601560248201527f535741505045525f544f4b454e5f494e5f5a45524f0000000000000000000000604482015260640161038d565b73ffffffffffffffffffffffffffffffffffffffff841661053c5760405162461bcd60e51b815260206004820152601660248201527f535741505045525f544f4b454e5f4f55545f5a45524f00000000000000000000604482015260640161038d565b6000831161058c5760405162461bcd60e51b815260206004820152601660248201527f535741505045525f414d4f554e545f494e5f5a45524f00000000000000000000604482015260640161038d565b600082116105dc5760405162461bcd60e51b815260206004820152601b60248201527f535741505045525f4d494e5f414d4f554e545f4f55545f5a45524f0000000000604482015260640161038d565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8716036107305783341461065e5760405162461bcd60e51b815260206004820152601860248201527f535741505045525f554e45585045435445445f56414c55450000000000000000604482015260640161038d565b6002546106819073ffffffffffffffffffffffffffffffffffffffff1685611048565b600254604080516000815260208101918290527f109b3c830000000000000000000000000000000000000000000000000000000090915273ffffffffffffffffffffffffffffffffffffffff9091169063109b3c83906106e6908790602481016119f2565b6020604051808303816000875af1158015610705573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107299190611a13565b905061091a565b341561077e5760405162461bcd60e51b815260206004820152601560248201527f535741505045525f56414c55455f47545f5a45524f0000000000000000000000604482015260640161038d565b6002546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9182166024820152859188169063dd62ed3e90604401602060405180830381865afa1580156107f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108199190611a13565b10156108675760405162461bcd60e51b815260206004820152601560248201527f535741505045525f4241445f414c4c4f57414e43450000000000000000000000604482015260640161038d565b60025473ffffffffffffffffffffffffffffffffffffffff16635af547e687338760006040519080825280601f01601f1916602001820160405280156108b4576020820181803683370190505b506040518563ffffffff1660e01b81526004016108d49493929190611a2c565b6020604051808303816000875af11580156108f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109179190611a13565b90505b60008061092688611173565b9050600061093388611173565b90508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036109c057839250858310156109bb5760405162461bcd60e51b815260206004820152601b60248201527f535741505045525f575241505f4d494e5f414d4f554e545f4f55540000000000604482015260640161038d565b610a69565b6002546040517f37e0ac0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116906337e0ac0290610a23908d90869086908a906001908e908e90600401611a75565b6020604051808303816000875af1158015610a42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a669190611a13565b92505b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8a1614610aa25783610b4a565b600254604080516000815260208101918290527fb413148e0000000000000000000000000000000000000000000000000000000090915273ffffffffffffffffffffffffffffffffffffffff9091169063b413148e90610b07908790602481016119f2565b6020604051808303816000875af1158015610b26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b4a9190611a13565b60025490915073ffffffffffffffffffffffffffffffffffffffff16639003afee8a833360006040519080825280601f01601f191660200182016040528015610b9a576020820181803683370190505b506040518563ffffffff1660e01b8152600401610bba9493929190611b0c565b6020604051808303816000875af1158015610bd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bfd9190611a13565b506040517f68f46c45a243a0e9065a97649faf9a5afe1692f2679e650c2f853b9cd734cc0e90600090a1505060018055505050505050505050565b610c66336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b60025473ffffffffffffffffffffffffffffffffffffffff1615610ccc5760405162461bcd60e51b815260206004820152601760248201527f534d4152545f5641554c545f414c52454144595f534554000000000000000000604482015260640161038d565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f6a7abc646862eeb16d8760c0c56dff37a2d2c6b012a3419910005e57e503a08790600090a250565b610d69336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b610d71611293565b565b6060610d7f6003611310565b905090565b610db2336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b610dbc828261131d565b5050565b610dee336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b610d716113ca565b610e24336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b610dbc8282611439565b6000610e5e336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b60ff831115610eaf5760405162461bcd60e51b815260206004820152601960248201527f535741505045525f494e56414c49445f534f555243455f494400000000000000604482015260640161038d565b81610ec457610ebf6003846114e1565b610ecf565b610ecf6003846114ed565b90508015610f1457827fe5859e75f917b81be842c32c040715d360bde2ea905972baa6eee35dc322980483604051610f0b911515815260200190565b60405180910390a25b92915050565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f50c7a3d1a23c7ff4a61d37c3f2c4aeb36cf60b43ee893723db201d3eb941cbad602052604081205460ff1680610fc9575073ffffffffffffffffffffffffffffffffffffffff83166000908152602081815260408083207fffffffff000000000000000000000000000000000000000000000000000000008616845290915290205460ff165b9392505050565b6000610f146003836114f9565b60025474010000000000000000000000000000000000000000900460ff1615610d715760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015260640161038d565b804710156110985760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161038d565b60008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d80600081146110f2576040519150601f19603f3d011682016040523d82523d6000602084013e6110f7565b606091505b505090508061116e5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161038d565b505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8316146111ac5781610f14565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166317fcb39b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611219573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f149190611b4b565b6112478282610f1a565b610dbc5760405162461bcd60e51b815260206004820152601760248201527f415554485f53454e4445525f4e4f545f414c4c4f574544000000000000000000604482015260640161038d565b61129b611511565b600280547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b60606000610fc98361157b565b73ffffffffffffffffffffffffffffffffffffffff82166000818152602081815260408083207fffffffff0000000000000000000000000000000000000000000000000000000086168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055519182527fa2e97b9eea8d1168700e334304edd057dff89c0384ca6f920c73cc3e48c8e52391015b60405180910390a25050565b6113d2610fdd565b600280547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586112e63390565b73ffffffffffffffffffffffffffffffffffffffff82166000818152602081815260408083207fffffffff0000000000000000000000000000000000000000000000000000000086168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055519182527f7f06c1c34ad47198873da9dcde2e40904035d41b23da3a9132df674a75022df791016113be565b6000610fc983836115d7565b6000610fc983836116ca565b60008181526001830160205260408120541515610fc9565b60025474010000000000000000000000000000000000000000900460ff16610d715760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f7420706175736564000000000000000000000000604482015260640161038d565b6060816000018054806020026020016040519081016040528092919081815260200182805480156115cb57602002820191906000526020600020905b8154815260200190600101908083116115b7575b50505050509050919050565b600081815260018301602052604081205480156116c05760006115fb600183611b68565b855490915060009061160f90600190611b68565b905081811461167457600086600001828154811061162f5761162f611ba2565b906000526020600020015490508087600001848154811061165257611652611ba2565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061168557611685611bd1565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610f14565b6000915050610f14565b600081815260018301602052604081205461171157508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610f14565b506000610f14565b73ffffffffffffffffffffffffffffffffffffffff8116811461173b57600080fd5b50565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060008060c0878903121561178657600080fd5b863560ff8116811461179757600080fd5b955060208701356117a781611719565b945060408701356117b781611719565b9350606087013592506080870135915060a087013567ffffffffffffffff808211156117e257600080fd5b818901915089601f8301126117f657600080fd5b8135818111156118085761180861173e565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561184e5761184e61173e565b816040528281528c602084870101111561186757600080fd5b8260208601602083013760006020848301015280955050505050509295509295509295565b60006020828403121561189e57600080fd5b8135610fc981611719565b6020808252825182820181905260009190848201906040850190845b818110156118e1578351835292840192918401916001016118c5565b50909695505050505050565b6000806040838503121561190057600080fd5b823561190b81611719565b915060208301357fffffffff000000000000000000000000000000000000000000000000000000008116811461194057600080fd5b809150509250929050565b6000806040838503121561195e57600080fd5b823591506020830135801515811461194057600080fd5b60006020828403121561198757600080fd5b5035919050565b6000815180845260005b818110156119b457602081850181015186830182015201611998565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b828152604060208201526000611a0b604083018461198e565b949350505050565b600060208284031215611a2557600080fd5b5051919050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152611a6b608083018461198e565b9695505050505050565b60ff8816815273ffffffffffffffffffffffffffffffffffffffff87811660208301528616604082015260608101859052600060028510611adf577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8460808301528360a083015260e060c0830152611aff60e083018461198e565b9998505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808716835285602084015280851660408401525060806060830152611a6b608083018461198e565b600060208284031215611b5d57600080fd5b8151610fc981611719565b81810381811115610f14577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea264697066735822122068ebc7aa223c90a7a767f1063ed35ca5729a9ddde779d68fca67123ad685a58564736f6c63430008110033000000000000000000000000028dbdc93fd8c9294ba61bf145f8384cd4385c7b000000000000000000000000de6d4872c0c8167fb4f405c7854fd2fed7edca21

Deployed Bytecode

0x6080604052600436106100e85760003560e01c80635c975abb1161008a578063933f0f3c11610059578063933f0f3c146102bf578063b771ed9f146102df578063d9972b96146102ff578063f157fac21461031f57600080fd5b80635c975abb1461021a5780636646a436146102565780637b103999146102765780638456cb59146102aa57600080fd5b80633f81a192116100c65780633f81a1921461013757806344ba1fca14610189578063452d98c7146101cb5780634fd49efd146101ed57600080fd5b8063049639fb146100ed57806313e3b57c146101025780633f4ba83a14610122575b600080fd5b6101006100fb36600461176d565b61033f565b005b34801561010e57600080fd5b5061010061011d36600461188c565b610c38565b34801561012e57600080fd5b50610100610d3b565b34801561014357600080fd5b5061015f73ffffffffffffffffffffffffffffffffffffffff81565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561019557600080fd5b506101bd7ff66b632ae3b479ef109dee46eb319414289a8426a08b64d81aad232941f9d22a81565b604051908152602001610180565b3480156101d757600080fd5b506101e0610d73565b60405161018091906118a9565b3480156101f957600080fd5b5060025461015f9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561022657600080fd5b5060025474010000000000000000000000000000000000000000900460ff165b6040519015158152602001610180565b34801561026257600080fd5b506101006102713660046118ed565b610d84565b34801561028257600080fd5b5061015f7f000000000000000000000000de6d4872c0c8167fb4f405c7854fd2fed7edca2181565b3480156102b657600080fd5b50610100610dc0565b3480156102cb57600080fd5b506101006102da3660046118ed565b610df6565b3480156102eb57600080fd5b506102466102fa36600461194b565b610e2e565b34801561030b57600080fd5b5061024661031a3660046118ed565b610f1a565b34801561032b57600080fd5b5061024661033a366004611975565b610fd0565b6002600154036103965760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60026001556103a3610fdd565b6103af8660ff16610fd0565b6103fb5760405162461bcd60e51b815260206004820152601a60248201527f535741505045525f534f555243455f4e4f545f414c4c4f574544000000000000604482015260640161038d565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16036104765760405162461bcd60e51b815260206004820152601360248201527f535741505045525f53414d455f544f4b454e5300000000000000000000000000604482015260640161038d565b73ffffffffffffffffffffffffffffffffffffffff85166104d95760405162461bcd60e51b815260206004820152601560248201527f535741505045525f544f4b454e5f494e5f5a45524f0000000000000000000000604482015260640161038d565b73ffffffffffffffffffffffffffffffffffffffff841661053c5760405162461bcd60e51b815260206004820152601660248201527f535741505045525f544f4b454e5f4f55545f5a45524f00000000000000000000604482015260640161038d565b6000831161058c5760405162461bcd60e51b815260206004820152601660248201527f535741505045525f414d4f554e545f494e5f5a45524f00000000000000000000604482015260640161038d565b600082116105dc5760405162461bcd60e51b815260206004820152601b60248201527f535741505045525f4d494e5f414d4f554e545f4f55545f5a45524f0000000000604482015260640161038d565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8716036107305783341461065e5760405162461bcd60e51b815260206004820152601860248201527f535741505045525f554e45585045435445445f56414c55450000000000000000604482015260640161038d565b6002546106819073ffffffffffffffffffffffffffffffffffffffff1685611048565b600254604080516000815260208101918290527f109b3c830000000000000000000000000000000000000000000000000000000090915273ffffffffffffffffffffffffffffffffffffffff9091169063109b3c83906106e6908790602481016119f2565b6020604051808303816000875af1158015610705573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107299190611a13565b905061091a565b341561077e5760405162461bcd60e51b815260206004820152601560248201527f535741505045525f56414c55455f47545f5a45524f0000000000000000000000604482015260640161038d565b6002546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9182166024820152859188169063dd62ed3e90604401602060405180830381865afa1580156107f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108199190611a13565b10156108675760405162461bcd60e51b815260206004820152601560248201527f535741505045525f4241445f414c4c4f57414e43450000000000000000000000604482015260640161038d565b60025473ffffffffffffffffffffffffffffffffffffffff16635af547e687338760006040519080825280601f01601f1916602001820160405280156108b4576020820181803683370190505b506040518563ffffffff1660e01b81526004016108d49493929190611a2c565b6020604051808303816000875af11580156108f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109179190611a13565b90505b60008061092688611173565b9050600061093388611173565b90508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036109c057839250858310156109bb5760405162461bcd60e51b815260206004820152601b60248201527f535741505045525f575241505f4d494e5f414d4f554e545f4f55540000000000604482015260640161038d565b610a69565b6002546040517f37e0ac0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116906337e0ac0290610a23908d90869086908a906001908e908e90600401611a75565b6020604051808303816000875af1158015610a42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a669190611a13565b92505b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8a1614610aa25783610b4a565b600254604080516000815260208101918290527fb413148e0000000000000000000000000000000000000000000000000000000090915273ffffffffffffffffffffffffffffffffffffffff9091169063b413148e90610b07908790602481016119f2565b6020604051808303816000875af1158015610b26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b4a9190611a13565b60025490915073ffffffffffffffffffffffffffffffffffffffff16639003afee8a833360006040519080825280601f01601f191660200182016040528015610b9a576020820181803683370190505b506040518563ffffffff1660e01b8152600401610bba9493929190611b0c565b6020604051808303816000875af1158015610bd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bfd9190611a13565b506040517f68f46c45a243a0e9065a97649faf9a5afe1692f2679e650c2f853b9cd734cc0e90600090a1505060018055505050505050505050565b610c66336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b60025473ffffffffffffffffffffffffffffffffffffffff1615610ccc5760405162461bcd60e51b815260206004820152601760248201527f534d4152545f5641554c545f414c52454144595f534554000000000000000000604482015260640161038d565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f6a7abc646862eeb16d8760c0c56dff37a2d2c6b012a3419910005e57e503a08790600090a250565b610d69336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b610d71611293565b565b6060610d7f6003611310565b905090565b610db2336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b610dbc828261131d565b5050565b610dee336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b610d716113ca565b610e24336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b610dbc8282611439565b6000610e5e336000357fffffffff000000000000000000000000000000000000000000000000000000001661123d565b60ff831115610eaf5760405162461bcd60e51b815260206004820152601960248201527f535741505045525f494e56414c49445f534f555243455f494400000000000000604482015260640161038d565b81610ec457610ebf6003846114e1565b610ecf565b610ecf6003846114ed565b90508015610f1457827fe5859e75f917b81be842c32c040715d360bde2ea905972baa6eee35dc322980483604051610f0b911515815260200190565b60405180910390a25b92915050565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f50c7a3d1a23c7ff4a61d37c3f2c4aeb36cf60b43ee893723db201d3eb941cbad602052604081205460ff1680610fc9575073ffffffffffffffffffffffffffffffffffffffff83166000908152602081815260408083207fffffffff000000000000000000000000000000000000000000000000000000008616845290915290205460ff165b9392505050565b6000610f146003836114f9565b60025474010000000000000000000000000000000000000000900460ff1615610d715760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015260640161038d565b804710156110985760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161038d565b60008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d80600081146110f2576040519150601f19603f3d011682016040523d82523d6000602084013e6110f7565b606091505b505090508061116e5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161038d565b505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8316146111ac5781610f14565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166317fcb39b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611219573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f149190611b4b565b6112478282610f1a565b610dbc5760405162461bcd60e51b815260206004820152601760248201527f415554485f53454e4445525f4e4f545f414c4c4f574544000000000000000000604482015260640161038d565b61129b611511565b600280547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b60606000610fc98361157b565b73ffffffffffffffffffffffffffffffffffffffff82166000818152602081815260408083207fffffffff0000000000000000000000000000000000000000000000000000000086168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055519182527fa2e97b9eea8d1168700e334304edd057dff89c0384ca6f920c73cc3e48c8e52391015b60405180910390a25050565b6113d2610fdd565b600280547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586112e63390565b73ffffffffffffffffffffffffffffffffffffffff82166000818152602081815260408083207fffffffff0000000000000000000000000000000000000000000000000000000086168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055519182527f7f06c1c34ad47198873da9dcde2e40904035d41b23da3a9132df674a75022df791016113be565b6000610fc983836115d7565b6000610fc983836116ca565b60008181526001830160205260408120541515610fc9565b60025474010000000000000000000000000000000000000000900460ff16610d715760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f7420706175736564000000000000000000000000604482015260640161038d565b6060816000018054806020026020016040519081016040528092919081815260200182805480156115cb57602002820191906000526020600020905b8154815260200190600101908083116115b7575b50505050509050919050565b600081815260018301602052604081205480156116c05760006115fb600183611b68565b855490915060009061160f90600190611b68565b905081811461167457600086600001828154811061162f5761162f611ba2565b906000526020600020015490508087600001848154811061165257611652611ba2565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061168557611685611bd1565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610f14565b6000915050610f14565b600081815260018301602052604081205461171157508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610f14565b506000610f14565b73ffffffffffffffffffffffffffffffffffffffff8116811461173b57600080fd5b50565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060008060c0878903121561178657600080fd5b863560ff8116811461179757600080fd5b955060208701356117a781611719565b945060408701356117b781611719565b9350606087013592506080870135915060a087013567ffffffffffffffff808211156117e257600080fd5b818901915089601f8301126117f657600080fd5b8135818111156118085761180861173e565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561184e5761184e61173e565b816040528281528c602084870101111561186757600080fd5b8260208601602083013760006020848301015280955050505050509295509295509295565b60006020828403121561189e57600080fd5b8135610fc981611719565b6020808252825182820181905260009190848201906040850190845b818110156118e1578351835292840192918401916001016118c5565b50909695505050505050565b6000806040838503121561190057600080fd5b823561190b81611719565b915060208301357fffffffff000000000000000000000000000000000000000000000000000000008116811461194057600080fd5b809150509250929050565b6000806040838503121561195e57600080fd5b823591506020830135801515811461194057600080fd5b60006020828403121561198757600080fd5b5035919050565b6000815180845260005b818110156119b457602081850181015186830182015201611998565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b828152604060208201526000611a0b604083018461198e565b949350505050565b600060208284031215611a2557600080fd5b5051919050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152611a6b608083018461198e565b9695505050505050565b60ff8816815273ffffffffffffffffffffffffffffffffffffffff87811660208301528616604082015260608101859052600060028510611adf577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8460808301528360a083015260e060c0830152611aff60e083018461198e565b9998505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808716835285602084015280851660408401525060806060830152611a6b608083018461198e565b600060208284031215611b5d57600080fd5b8151610fc981611719565b81810381811115610f14577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea264697066735822122068ebc7aa223c90a7a767f1063ed35ca5729a9ddde779d68fca67123ad685a58564736f6c63430008110033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000028dbdc93fd8c9294ba61bf145f8384cd4385c7b000000000000000000000000de6d4872c0c8167fb4f405c7854fd2fed7edca21

-----Decoded View---------------
Arg [0] : admin (address): 0x028DBDC93fd8C9294ba61bF145F8384Cd4385c7b
Arg [1] : registry (address): 0xde6D4872c0C8167fB4F405C7854FD2fED7edca21

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000028dbdc93fd8c9294ba61bf145f8384cd4385c7b
Arg [1] : 000000000000000000000000de6d4872c0c8167fb4f405c7854fd2fed7edca21


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Chain Token Portfolio % Price Amount Value
BSC18.66%<$0.0000013,750,971,392.5698$448.8
BSC9.11%$0.0001731,267,236.4267$219.11
BSC4.20%$0.590446171.2142$101.09
BSC2.84%$5.7411.9$68.33
BSC2.40%<$0.000001130,924,979,611.1$57.66
BSC1.79%$142.969$42.99
BSC1.28%$0.99967330.687$30.68
BSC1.21%$5.835$29.13
BSC1.08%$0.000089290,289.9891$25.87
BSC0.91%<$0.00000119,807,224,467.247$21.84
BSC0.42%<$0.0000014,032,395,844,464.84$10.11
BSC0.32%$181.360.0425$7.71
BSC0.23%$0.13762540.1703$5.53
BSC0.21%$6.80.7355$5
BSC0.11%<$0.0000012,202,375,770.6977$2.73
BSC0.07%<$0.0000011,299,377,696.8702$1.79
BSC0.06%<$0.0000011,307,130,100.0429$1.48
BSC0.04%<$0.0000014,001,868.7803$0.9205
BSC0.03%$0.3696132.2518$0.8322
BSC<0.01%$1.970.11$0.2167
ETH22.85%$3.27168.1$549.6
ETH0.47%$0.49045422.9317$11.25
ETH0.43%$2.883.5844$10.34
ETH0.40%<$0.000001217,153,329$9.52
ETH0.19%$0.5111499$4.6
ETH0.08%<$0.0000015,405,171,727.184$1.92
MATIC12.91%$1310.0541$310.67
MATIC6.25%$0.99994150.2997$150.29
ARB11.44%$0.724584379.8444$275.23
ZKSYNC0.01%$3,250.980.0001$0.325098
MANTLE<0.01%$0.8537490.015$0.012806
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.