Contract

0x41fFb31bDBa1278C073C1018BB988C5368b9b10c

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

-

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
4342712024-12-15 7:49:0715 days ago1734248947  Contract Creation0 S
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FluidReserveContract

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 10000000 runs

Other Settings:
paris EvmVersion
File 1 of 33 : main.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import { IFTokenAdmin } from "../protocols/lending/interfaces/iFToken.sol";
import { IFluidVaultT1 } from "../protocols/vault/interfaces/iVaultT1.sol";
import { IFluidVault } from "../protocols/vault/interfaces/iVault.sol";
import { SafeTransfer } from "../libraries/safeTransfer.sol";

import { Variables } from "./variables.sol";
import { Events } from "./events.sol";
import { ErrorTypes } from "./errorTypes.sol";
import { Error } from "./error.sol";

abstract contract ReserveContractAuth is Variables, Error, Events {
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @dev validates that an address is not the zero address
    modifier validAddress(address value_) {
        if (value_ == address(0)) {
            revert FluidReserveContractError(ErrorTypes.ReserveContract__AddressZero);
        }
        _;
    }

    /// @notice Checks that the sender is an auth
    modifier onlyAuth() {
        if (!isAuth[msg.sender] && owner() != msg.sender)
            revert FluidReserveContractError(ErrorTypes.ReserveContract__Unauthorized);
        _;
    }

    /// @notice              Updates an auth's status as an auth
    /// @param auth_         The address to update
    /// @param isAuth_       Whether or not the address should be an auth
    function updateAuth(address auth_, bool isAuth_) external onlyOwner validAddress(auth_) {
        isAuth[auth_] = isAuth_;
        emit LogUpdateAuth(auth_, isAuth_);
    }

    /// @notice                 Updates a rebalancer's status as a rebalancer
    /// @param rebalancer_      The address to update
    /// @param isRebalancer_    Whether or not the address should be a rebalancer
    function updateRebalancer(address rebalancer_, bool isRebalancer_) external onlyAuth validAddress(rebalancer_) {
        isRebalancer[rebalancer_] = isRebalancer_;
        emit LogUpdateRebalancer(rebalancer_, isRebalancer_);
    }

    /// @notice              Approves protocols to spend the reserves tokens
    /// @dev                 The parameters are parallel arrays
    /// @param protocols_    The protocols that will be spending reserve tokens
    /// @param tokens_       The tokens to approve
    /// @param amounts_      The amounts to approve
    function approve(
        address[] memory protocols_,
        address[] memory tokens_,
        uint256[] memory amounts_
    ) external onlyAuth {
        if (protocols_.length != tokens_.length || tokens_.length != amounts_.length) {
            revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);
        }

        for (uint256 i = 0; i < protocols_.length; i++) {
            address protocol_ = protocols_[i];
            address token_ = tokens_[i];
            uint256 amount_ = amounts_[i];
            uint256 existingAllowance_;

            if (token_ == NATIVE_TOKEN_ADDRESS) {
                existingAllowance_ = nativeTokenAllowances[protocol_];
                _approveNativeToken(protocol_, amount_);
            } else {
                existingAllowance_ = IERC20(token_).allowance(address(this), protocol_);

                // making approval 0 first and then re-approving with a new amount.
                SafeERC20.safeApprove(IERC20(address(token_)), protocol_, 0);
                SafeERC20.safeApprove(IERC20(address(token_)), protocol_, amount_);
            }
            _protocolTokens[protocol_].add(token_);

            _protocols.add(protocol_);
            emit LogAllow(protocol_, token_, amount_, existingAllowance_);
        }
    }

    /// @notice              Revokes protocols' ability to spend the reserves tokens
    /// @dev                 The parameters are parallel arrays
    /// @param protocols_    The protocols that will no longer be spending reserve tokens
    /// @param tokens_       The tokens to revoke
    function revoke(address[] memory protocols_, address[] memory tokens_) external onlyAuth {
        if (protocols_.length != tokens_.length) {
            revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);
        }

        for (uint256 i = 0; i < protocols_.length; i++) {
            address protocol_ = protocols_[i];
            address token_ = tokens_[i];

            if (token_ == NATIVE_TOKEN_ADDRESS) {
                _approveNativeToken(protocol_, 0);
            } else {
                SafeERC20.safeApprove(IERC20(address(token_)), protocol_, 0);
            }
            _protocolTokens[protocol_].remove(token_);

            if (_protocolTokens[protocol_].length() == 0) {
                _protocols.remove(protocol_);
            }
            emit LogRevoke(protocol_, token_);
        }
    }

    function _approveNativeToken(address protocol_, uint256 amount_) internal {
        nativeTokenAllowances[protocol_] = amount_;
    }
}

/// @title    Reserve Contract
/// @notice   This contract manages the approval of tokens for use by protocols and
///           the execution of rebalances on protocols
contract FluidReserveContract is Error, ReserveContractAuth, UUPSUpgradeable {
    using EnumerableSet for EnumerableSet.AddressSet;
    using SafeERC20 for IERC20;

    /// @notice Checks that the sender is a rebalancer
    modifier onlyRebalancer() {
        if (!isRebalancer[msg.sender]) revert FluidReserveContractError(ErrorTypes.ReserveContract__Unauthorized);
        _;
    }

    constructor() {
        // ensure logic contract initializer is not abused by disabling initializing
        // see https://forum.openzeppelin.com/t/security-advisory-initialize-uups-implementation-contracts/15301
        // and https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract
        _disableInitializers();
    }

    /// @notice initializes the contract
    /// @param _auths  The addresses that have the auth to approve and revoke protocol token allowances
    /// @param _rebalancers  The addresses that can execute a rebalance on a protocol
    /// @param owner_  owner address is able to upgrade contract and update auth users
    function initialize(
        address[] memory _auths,
        address[] memory _rebalancers,
        address owner_
    ) public initializer validAddress(owner_) {
        for (uint256 i = 0; i < _auths.length; i++) {
            isAuth[_auths[i]] = true;
            emit LogUpdateAuth(_auths[i], true);
        }
        for (uint256 i = 0; i < _rebalancers.length; i++) {
            isRebalancer[_rebalancers[i]] = true;
            emit LogUpdateRebalancer(_rebalancers[i], true);
        }
        _transferOwnership(owner_);
    }

    function _authorizeUpgrade(address) internal override onlyOwner {}

    /// @notice override renounce ownership as it could leave the contract in an unwanted state if called by mistake.
    function renounceOwnership() public view override onlyOwner {
        revert FluidReserveContractError(ErrorTypes.ReserveContract__RenounceOwnershipUnsupported);
    }

    /// @notice              Executes a rebalance on a fToken protocol by calling that protocol's `rebalance` function
    /// @param protocol_     The protocol to rebalance
    /// @param value_        any msg.value to send along (as fetched from resolver!)
    function rebalanceFToken(address protocol_, uint256 value_) public payable onlyRebalancer {
        if (value_ > 0) {
            if (nativeTokenAllowances[protocol_] < value_) {
                revert FluidReserveContractError(ErrorTypes.ReserveContract__InsufficientAllowance);
            }
            nativeTokenAllowances[protocol_] -= value_;
        }

        uint256 amount_ = IFTokenAdmin(protocol_).rebalance{ value: value_ }();
        emit LogRebalanceFToken(protocol_, amount_);
    }

    /// @notice              Executes a rebalance on a vaultT1 protocol by calling that protocol's `rebalance` function
    /// @param protocol_     The protocol to rebalance
    /// @param value_        any msg.value to send along (as fetched from resolver!)
    function rebalanceVault(address protocol_, uint256 value_) public payable onlyRebalancer {
        if (value_ > 0) {
            if (nativeTokenAllowances[protocol_] < value_) {
                revert FluidReserveContractError(ErrorTypes.ReserveContract__InsufficientAllowance);
            }
            nativeTokenAllowances[protocol_] -= value_;
        }

        (int256 colAmount_, int256 debtAmount_) = IFluidVaultT1(protocol_).rebalance{ value: value_ }();

        if (value_ > 0) {
            IFluidVaultT1.ConstantViews memory constants_ = IFluidVaultT1(protocol_).constantsView();
            if (constants_.supplyToken == NATIVE_TOKEN_ADDRESS && colAmount_ < 0) {
                revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);
            }

            if (constants_.borrowToken == NATIVE_TOKEN_ADDRESS && debtAmount_ > 0) {
                revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);
            }

            if (!(constants_.supplyToken == NATIVE_TOKEN_ADDRESS || constants_.borrowToken == NATIVE_TOKEN_ADDRESS)) {
                revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);
            }
        }

        emit LogRebalanceVault(protocol_, colAmount_, debtAmount_);
    }

    /// @notice              Executes a rebalance on a DEX vault protocol by calling that protocol's `rebalance` function
    /// @param protocol_     The protocol to rebalance
    /// @param value_        any msg.value to send along (as fetched from resolver!)
    /// @param colToken0MinMax_ if vault supply is more than Liquidity Layer then deposit difference through reserve/rebalance contract
    /// @param colToken1MinMax_ if vault supply is less than Liquidity Layer then withdraw difference to reserve/rebalance contract
    /// @param debtToken0MinMax_ if vault borrow is more than Liquidity Layer then borrow difference to reserve/rebalance contract
    /// @param debtToken1MinMax_ if vault borrow is less than Liquidity Layer then payback difference through reserve/rebalance contract
    function rebalanceDexVault(
        address protocol_,
        uint256 value_,
        int colToken0MinMax_,
        int colToken1MinMax_,
        int debtToken0MinMax_,
        int debtToken1MinMax_
    ) public payable onlyRebalancer {
        uint256 initialBalance_ = address(this).balance;
        if (value_ > 0 && nativeTokenAllowances[protocol_] < value_) {
            revert FluidReserveContractError(ErrorTypes.ReserveContract__InsufficientAllowance);
        }

        (int256 colAmount_, int256 debtAmount_) = IFluidVault(protocol_).rebalance{ value: value_ }(
            colToken0MinMax_,
            colToken1MinMax_,
            debtToken0MinMax_,
            debtToken1MinMax_
        );

        if (value_ > 0 && (colAmount_ > 0 || debtAmount_ < 0)) {
            // value was sent along and either deposit or payback happened. subtract the amount from allowance.
            // only substract actually used balance from allowance
            uint256 usedBalance_ = initialBalance_ > address(this).balance
                ? initialBalance_ - address(this).balance
                : 0;
            if (msg.value > 0) {
                usedBalance_ = usedBalance_ > msg.value ? usedBalance_ - msg.value : 0;
            }
            if (usedBalance_ > 0) {
                nativeTokenAllowances[protocol_] -= usedBalance_;
            }
        }

        emit LogRebalanceVault(protocol_, colAmount_, debtAmount_);
    }

    /// @notice calls `rebalanceFToken` multiple times
    /// @dev don't need onlyRebalancer modifier as it is already checked in `rebalanceFToken` function
    function rebalanceFTokens(address[] calldata protocols_, uint256[] calldata values_) external payable {
        if (protocols_.length != values_.length) {
            revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);
        }

        for (uint256 i = 0; i < protocols_.length; i++) {
            rebalanceFToken(protocols_[i], values_[i]);
        }
    }

    /// @notice calls `rebalanceVault` multiple times
    /// @dev  don't need onlyRebalancer modifier as it is already checked in `rebalanceVault` function
    function rebalanceVaults(address[] calldata protocols_, uint256[] calldata values_) external payable {
        if (protocols_.length != values_.length) {
            revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);
        }

        for (uint256 i = 0; i < protocols_.length; i++) {
            rebalanceVault(protocols_[i], values_[i]);
        }
    }

    /// @notice calls `rebalanceDexVault` multiple times
    /// @dev  don't need onlyRebalancer modifier as it is already checked in `rebalanceDexVault` function
    function rebalanceDexVaults(
        address[] calldata protocols_,
        uint256[] calldata values_,
        int[] calldata colToken0MinMaxs_,
        int[] calldata colToken1MinMaxs_,
        int[] calldata debtToken0MinMaxs_,
        int[] calldata debtToken1MinMaxs_
    ) external payable {
        if (
            protocols_.length != values_.length ||
            protocols_.length != colToken0MinMaxs_.length ||
            protocols_.length != colToken1MinMaxs_.length ||
            protocols_.length != debtToken0MinMaxs_.length ||
            protocols_.length != debtToken1MinMaxs_.length
        ) {
            revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);
        }

        for (uint256 i = 0; i < protocols_.length; i++) {
            rebalanceDexVault(
                protocols_[i],
                values_[i],
                colToken0MinMaxs_[i],
                colToken1MinMaxs_[i],
                debtToken0MinMaxs_[i],
                debtToken1MinMaxs_[i]
            );
        }
    }

    /// @notice              Withdraws funds from the contract to a specified receiver
    /// @param tokens_       The tokens to withdraw
    /// @param amounts_      The amounts of each token to withdraw
    /// @param receiver_     The address to receive the withdrawn funds
    /// @dev                 This function can only be called by the owner, which is always the Governance address
    function withdrawFunds(address[] memory tokens_, uint256[] memory amounts_, address receiver_) external onlyOwner {
        if (tokens_.length != amounts_.length) {
            revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);
        }

        for (uint256 i = 0; i < tokens_.length; i++) {
            if (tokens_[i] == NATIVE_TOKEN_ADDRESS) {
                SafeTransfer.safeTransferNative(receiver_, amounts_[i]);
            } else {
                SafeTransfer.safeTransfer(address(tokens_[i]), receiver_, amounts_[i]);
            }
            emit LogWithdrawFunds(tokens_[i], amounts_[i], receiver_);
        }
    }

    /// @notice              Gets the tokens that are approved for use by a protocol
    /// @param protocol_     The protocol to get the tokens for
    /// @return result_      The tokens that are approved for use by the protocol
    function getProtocolTokens(address protocol_) external view returns (address[] memory result_) {
        EnumerableSet.AddressSet storage tokens_ = _protocolTokens[protocol_];
        result_ = new address[](tokens_.length());
        for (uint256 i = 0; i < tokens_.length(); i++) {
            result_[i] = tokens_.at(i);
        }
    }

    /// @notice              Gets the allowances that are approved to a protocol
    /// @param protocol_     The protocol to get the tokens for
    /// @return allowances_  The tokens that are approved for use by the protocol
    function getProtocolAllowances(address protocol_) public view returns (TokenAllowance[] memory allowances_) {
        EnumerableSet.AddressSet storage tokens_ = _protocolTokens[protocol_];
        allowances_ = new TokenAllowance[](tokens_.length());
        for (uint256 i = 0; i < tokens_.length(); i++) {
            address token_ = tokens_.at(i);
            (allowances_[i]).token = token_;
            if (token_ == NATIVE_TOKEN_ADDRESS) {
                (allowances_[i]).allowance = nativeTokenAllowances[protocol_];
            } else {
                (allowances_[i]).allowance = IERC20(token_).allowance(address(this), protocol_);
            }
        }
    }

    /// @notice              Gets the allowances that are approved to a protocol
    /// @return allowances_  The tokens that are approved for use by all the protocols
    function getAllProtocolAllowances() public view returns (ProtocolTokenAllowance[] memory allowances_) {
        allowances_ = new ProtocolTokenAllowance[](_protocols.length());
        for (uint i = 0; i < _protocols.length(); i++) {
            address protocol_ = _protocols.at(i);
            (allowances_[i]).protocol = protocol_;
            (allowances_[i]).tokenAllowances = getProtocolAllowances(protocol_);
        }
    }

    /// @notice allow receive native token
    receive() external payable {}
}

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

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

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

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

    function __Ownable_init_unchained() internal onlyInitializing {
        _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);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 3 of 33 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.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.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.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.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(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.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

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

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

File 4 of 33 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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 5 of 33 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 6 of 33 : draft-IERC1822.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

File 7 of 33 : IERC4626.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 *
 * _Available since v4.7._
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);
}

File 8 of 33 : IBeacon.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 9 of 33 : ERC1967Upgrade.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeacon.sol";
import "../../interfaces/draft-IERC1822.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967Upgrade {
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallUUPS(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        // Upgrades from old implementations will perform a rollback test. This test requires the new
        // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
        // this special case will break upgrade paths from old UUPS implementation to new ones.
        if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
            _setImplementation(newImplementation);
        } else {
            try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            Address.isContract(IBeacon(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        }
    }
}

File 10 of 33 : UUPSUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.0;

import "../../interfaces/draft-IERC1822.sol";
import "../ERC1967/ERC1967Upgrade.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 *
 * _Available since v4.1._
 */
abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
    address private immutable __self = address(this);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        require(address(this) != __self, "Function must be called through delegatecall");
        require(_getImplementation() == __self, "Function must be called through active proxy");
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
        _;
    }

    /**
     * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
        return _IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeTo(address newImplementation) external virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data, true);
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeTo} and {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal override onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;
}

File 11 of 33 : 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 12 of 33 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 13 of 33 : 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 14 of 33 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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 15 of 33 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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 16 of 33 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }
}

File 17 of 33 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

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) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

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

        return result;
    }

    // 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 in 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 18 of 33 : iProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IProxy {
    function setAdmin(address newAdmin_) external;

    function setDummyImplementation(address newDummyImplementation_) external;

    function addImplementation(address implementation_, bytes4[] calldata sigs_) external;

    function removeImplementation(address implementation_) external;

    function getAdmin() external view returns (address);

    function getDummyImplementation() external view returns (address);

    function getImplementationSigs(address impl_) external view returns (bytes4[] memory);

    function getSigsImplementation(bytes4 sig_) external view returns (address);

    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
}

File 19 of 33 : errorTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

library LibsErrorTypes {
    /***********************************|
    |         LiquidityCalcs            | 
    |__________________________________*/

    /// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet)
    uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001;

    /// @notice thrown when rate data is set to a version that is not implemented
    uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002;

    /// @notice thrown when the calculated borrow rate turns negative. This should never happen.
    uint256 internal constant LiquidityCalcs__BorrowRateNegative = 70003;

    /***********************************|
    |           SafeTransfer            | 
    |__________________________________*/

    /// @notice thrown when safe transfer from for an ERC20 fails
    uint256 internal constant SafeTransfer__TransferFromFailed = 71001;

    /// @notice thrown when safe transfer for an ERC20 fails
    uint256 internal constant SafeTransfer__TransferFailed = 71002;

    /***********************************|
    |           SafeApprove             | 
    |__________________________________*/

    /// @notice thrown when safe approve from for an ERC20 fails
    uint256 internal constant SafeApprove__ApproveFailed = 81001;
}

File 20 of 33 : safeTransfer.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.21;

import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol";

/// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom
library SafeTransfer {
    uint256 internal constant MAX_NATIVE_TRANSFER_GAS = 20000; // pass max. 20k gas for native transfers

    error FluidSafeTransferError(uint256 errorId_);

    /// @dev Transfer `amount_` of `token_` from `from_` to `to_`, spending the approval given by `from_` to the
    /// calling contract. If `token_` returns no value, non-reverting calls are assumed to be successful.
    /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63
    function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from_" argument.
            mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
            mstore(add(freeMemoryPointer, 68), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.

            success_ := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed);
        }
    }

    /// @dev Transfer `amount_` of `token_` to `to_`.
    /// If `token_` returns no value, non-reverting calls are assumed to be successful.
    /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95
    function safeTransfer(address token_, address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
            mstore(add(freeMemoryPointer, 36), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.

            success_ := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
        }
    }

    /// @dev Transfer `amount_` of ` native token to `to_`.
    /// Minimally modified from Solmate SafeTransferLib (Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25
    function safeTransferNative(address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not. Pass limited gas
            success_ := call(MAX_NATIVE_TRANSFER_GAS, to_, amount_, 0, 0, 0, 0)
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
        }
    }
}

File 21 of 33 : structs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Structs {
    struct AddressBool {
        address addr;
        bool value;
    }

    struct AddressUint256 {
        address addr;
        uint256 value;
    }

    /// @notice struct to set borrow rate data for version 1
    struct RateDataV1Params {
        ///
        /// @param token for rate data
        address token;
        ///
        /// @param kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
        /// utilization below kink usually means slow increase in rate, once utilization is above kink borrow rate increases fast
        uint256 kink;
        ///
        /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
        /// i.e. constant minimum borrow rate
        /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
        uint256 rateAtUtilizationZero;
        ///
        /// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700
        uint256 rateAtUtilizationKink;
        ///
        /// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
        uint256 rateAtUtilizationMax;
    }

    /// @notice struct to set borrow rate data for version 2
    struct RateDataV2Params {
        ///
        /// @param token for rate data
        address token;
        ///
        /// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
        /// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster
        uint256 kink1;
        ///
        /// @param kink2 second kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
        /// utilization below kink 2 usually means slow / medium increase in rate, once utilization is above kink 2 borrow rate increases fast
        uint256 kink2;
        ///
        /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
        /// i.e. constant minimum borrow rate
        /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
        uint256 rateAtUtilizationZero;
        ///
        /// @param rateAtUtilizationKink1 desired borrow rate when utilization is at first kink. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 7% at first kink then rateAtUtilizationKink would be 700
        uint256 rateAtUtilizationKink1;
        ///
        /// @param rateAtUtilizationKink2 desired borrow rate when utilization is at second kink. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 7% at second kink then rateAtUtilizationKink would be 1_200
        uint256 rateAtUtilizationKink2;
        ///
        /// @param rateAtUtilizationMax desired borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
        uint256 rateAtUtilizationMax;
    }

    /// @notice struct to set token config
    struct TokenConfig {
        ///
        /// @param token address
        address token;
        ///
        /// @param fee charges on borrower's interest. in 1e2: 100% = 10_000; 1% = 100
        uint256 fee;
        ///
        /// @param threshold on when to update the storage slot. in 1e2: 100% = 10_000; 1% = 100
        uint256 threshold;
        ///
        /// @param maxUtilization maximum allowed utilization. in 1e2: 100% = 10_000; 1% = 100
        ///                       set to 100% to disable and have default limit of 100% (avoiding SLOAD).
        uint256 maxUtilization;
    }

    /// @notice struct to set user supply & withdrawal config
    struct UserSupplyConfig {
        ///
        /// @param user address
        address user;
        ///
        /// @param token address
        address token;
        ///
        /// @param mode: 0 = without interest. 1 = with interest
        uint8 mode;
        ///
        /// @param expandPercent withdrawal limit expand percent. in 1e2: 100% = 10_000; 1% = 100
        /// Also used to calculate rate at which withdrawal limit should decrease (instant).
        uint256 expandPercent;
        ///
        /// @param expandDuration withdrawal limit expand duration in seconds.
        /// used to calculate rate together with expandPercent
        uint256 expandDuration;
        ///
        /// @param baseWithdrawalLimit base limit, below this, user can withdraw the entire amount.
        /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
        /// with interest -> raw, without interest -> normal
        uint256 baseWithdrawalLimit;
    }

    /// @notice struct to set user borrow & payback config
    struct UserBorrowConfig {
        ///
        /// @param user address
        address user;
        ///
        /// @param token address
        address token;
        ///
        /// @param mode: 0 = without interest. 1 = with interest
        uint8 mode;
        ///
        /// @param expandPercent debt limit expand percent. in 1e2: 100% = 10_000; 1% = 100
        /// Also used to calculate rate at which debt limit should decrease (instant).
        uint256 expandPercent;
        ///
        /// @param expandDuration debt limit expand duration in seconds.
        /// used to calculate rate together with expandPercent
        uint256 expandDuration;
        ///
        /// @param baseDebtCeiling base borrow limit. until here, borrow limit remains as baseDebtCeiling
        /// (user can borrow until this point at once without stepped expansion). Above this, automated limit comes in place.
        /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
        /// with interest -> raw, without interest -> normal
        uint256 baseDebtCeiling;
        ///
        /// @param maxDebtCeiling max borrow ceiling, maximum amount the user can borrow.
        /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
        /// with interest -> raw, without interest -> normal
        uint256 maxDebtCeiling;
    }
}

File 22 of 33 : iLiquidity.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { IProxy } from "../../infiniteProxy/interfaces/iProxy.sol";
import { Structs as AdminModuleStructs } from "../adminModule/structs.sol";

interface IFluidLiquidityAdmin {
    /// @notice adds/removes auths. Auths generally could be contracts which can have restricted actions defined on contract.
    ///         auths can be helpful in reducing governance overhead where it's not needed.
    /// @param authsStatus_ array of structs setting allowed status for an address.
    ///                     status true => add auth, false => remove auth
    function updateAuths(AdminModuleStructs.AddressBool[] calldata authsStatus_) external;

    /// @notice adds/removes guardians. Only callable by Governance.
    /// @param guardiansStatus_ array of structs setting allowed status for an address.
    ///                         status true => add guardian, false => remove guardian
    function updateGuardians(AdminModuleStructs.AddressBool[] calldata guardiansStatus_) external;

    /// @notice changes the revenue collector address (contract that is sent revenue). Only callable by Governance.
    /// @param revenueCollector_  new revenue collector address
    function updateRevenueCollector(address revenueCollector_) external;

    /// @notice changes current status, e.g. for pausing or unpausing all user operations. Only callable by Auths.
    /// @param newStatus_ new status
    ///        status = 2 -> pause, status = 1 -> resume.
    function changeStatus(uint256 newStatus_) external;

    /// @notice                  update tokens rate data version 1. Only callable by Auths.
    /// @param tokensRateData_   array of RateDataV1Params with rate data to set for each token
    function updateRateDataV1s(AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_) external;

    /// @notice                  update tokens rate data version 2. Only callable by Auths.
    /// @param tokensRateData_   array of RateDataV2Params with rate data to set for each token
    function updateRateDataV2s(AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_) external;

    /// @notice updates token configs: fee charge on borrowers interest & storage update utilization threshold.
    ///         Only callable by Auths.
    /// @param tokenConfigs_ contains token address, fee & utilization threshold
    function updateTokenConfigs(AdminModuleStructs.TokenConfig[] calldata tokenConfigs_) external;

    /// @notice updates user classes: 0 is for new protocols, 1 is for established protocols.
    ///         Only callable by Auths.
    /// @param userClasses_ struct array of uint256 value to assign for each user address
    function updateUserClasses(AdminModuleStructs.AddressUint256[] calldata userClasses_) external;

    /// @notice sets user supply configs per token basis. Eg: with interest or interest-free and automated limits.
    ///         Only callable by Auths.
    /// @param userSupplyConfigs_ struct array containing user supply config, see `UserSupplyConfig` struct for more info
    function updateUserSupplyConfigs(AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_) external;

    /// @notice sets a new withdrawal limit as the current limit for a certain user
    /// @param user_ user address for which to update the withdrawal limit
    /// @param token_ token address for which to update the withdrawal limit
    /// @param newLimit_ new limit until which user supply can decrease to.
    ///                  Important: input in raw. Must account for exchange price in input param calculation.
    ///                  Note any limit that is < max expansion or > current user supply will set max expansion limit or
    ///                  current user supply as limit respectively.
    ///                  - set 0 to make maximum possible withdrawable: instant full expansion, and if that goes
    ///                  below base limit then fully down to 0.
    ///                  - set type(uint256).max to make current withdrawable 0 (sets current user supply as limit).
    function updateUserWithdrawalLimit(address user_, address token_, uint256 newLimit_) external;

    /// @notice setting user borrow configs per token basis. Eg: with interest or interest-free and automated limits.
    ///         Only callable by Auths.
    /// @param userBorrowConfigs_ struct array containing user borrow config, see `UserBorrowConfig` struct for more info
    function updateUserBorrowConfigs(AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_) external;

    /// @notice pause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
    /// Only callable by Guardians.
    /// @param user_          address of user to pause operations for
    /// @param supplyTokens_  token addresses to pause withdrawals for
    /// @param borrowTokens_  token addresses to pause borrowings for
    function pauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;

    /// @notice unpause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
    /// Only callable by Guardians.
    /// @param user_          address of user to unpause operations for
    /// @param supplyTokens_  token addresses to unpause withdrawals for
    /// @param borrowTokens_  token addresses to unpause borrowings for
    function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;

    /// @notice         collects revenue for tokens to configured revenueCollector address.
    /// @param tokens_  array of tokens to collect revenue for
    /// @dev            Note that this can revert if token balance is < revenueAmount (utilization > 100%)
    function collectRevenue(address[] calldata tokens_) external;

    /// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage.
    /// @param tokens_ tokens to update exchange prices for
    /// @return supplyExchangePrices_ new supply rates of overall system for each token
    /// @return borrowExchangePrices_ new borrow rates of overall system for each token
    function updateExchangePrices(
        address[] calldata tokens_
    ) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_);
}

interface IFluidLiquidityLogic is IFluidLiquidityAdmin {
    /// @notice Single function which handles supply, withdraw, borrow & payback
    /// @param token_ address of token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native)
    /// @param supplyAmount_ if +ve then supply, if -ve then withdraw, if 0 then nothing
    /// @param borrowAmount_ if +ve then borrow, if -ve then payback, if 0 then nothing
    /// @param withdrawTo_ if withdrawal then to which address
    /// @param borrowTo_ if borrow then to which address
    /// @param callbackData_ callback data passed to `liquidityCallback` method of protocol
    /// @return memVar3_ updated supplyExchangePrice
    /// @return memVar4_ updated borrowExchangePrice
    /// @dev to trigger skipping in / out transfers (gas optimization):
    /// -  ` callbackData_` MUST be encoded so that "from" address is the last 20 bytes in the last 32 bytes slot,
    ///     also for native token operations where liquidityCallback is not triggered!
    ///     from address must come at last position if there is more data. I.e. encode like:
    ///     abi.encode(otherVar1, otherVar2, FROM_ADDRESS). Note dynamic types used with abi.encode come at the end
    ///     so if dynamic types are needed, you must use abi.encodePacked to ensure the from address is at the end.
    /// -   this "from" address must match withdrawTo_ or borrowTo_ and must be == `msg.sender`
    /// -   `callbackData_` must in addition to the from address as described above include bytes32 SKIP_TRANSFERS
    ///     in the slot before (bytes 32 to 63)
    /// -   `msg.value` must be 0.
    /// -   Amounts must be either:
    ///     -  supply(+) == borrow(+), withdraw(-) == payback(-).
    ///     -  Liquidity must be on the winning side (deposit < borrow OR payback < withdraw).
    function operate(
        address token_,
        int256 supplyAmount_,
        int256 borrowAmount_,
        address withdrawTo_,
        address borrowTo_,
        bytes calldata callbackData_
    ) external payable returns (uint256 memVar3_, uint256 memVar4_);
}

interface IFluidLiquidity is IProxy, IFluidLiquidityLogic {}

File 23 of 33 : iFToken.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";

import { IAllowanceTransfer } from "./permit2/iAllowanceTransfer.sol";
import { IFluidLendingRewardsRateModel } from "./iLendingRewardsRateModel.sol";
import { IFluidLendingFactory } from "./iLendingFactory.sol";
import { IFluidLiquidity } from "../../../liquidity/interfaces/iLiquidity.sol";

interface IFTokenAdmin {
    /// @notice updates the rewards rate model contract.
    ///         Only callable by LendingFactory auths.
    /// @param rewardsRateModel_  the new rewards rate model contract address.
    ///                           can be set to address(0) to set no rewards (to save gas)
    function updateRewards(IFluidLendingRewardsRateModel rewardsRateModel_) external;

    /// @notice Balances out the difference between fToken supply at Liquidity vs totalAssets().
    ///         Deposits underlying from rebalancer address into Liquidity but doesn't mint any shares
    ///         -> thus making deposit available as rewards.
    ///         Only callable by rebalancer.
    /// @return assets_ amount deposited to Liquidity
    function rebalance() external payable returns (uint256 assets_);

    /// @notice gets the liquidity exchange price of the underlying asset, calculates the updated exchange price (with reward rates)
    ///         and writes those values to storage.
    ///         Callable by anyone.
    /// @return tokenExchangePrice_ exchange price of fToken share to underlying asset
    /// @return liquidityExchangePrice_ exchange price at Liquidity for the underlying asset
    function updateRates() external returns (uint256 tokenExchangePrice_, uint256 liquidityExchangePrice_);

    /// @notice sends any potentially stuck funds to Liquidity contract. Only callable by LendingFactory auths.
    function rescueFunds(address token_) external;

    /// @notice Updates the rebalancer address (ReserveContract). Only callable by LendingFactory auths.
    function updateRebalancer(address rebalancer_) external;
}

interface IFToken is IERC4626, IFTokenAdmin {
    /// @notice returns minimum amount required for deposit (rounded up)
    function minDeposit() external view returns (uint256);

    /// @notice returns config, rewards and exchange prices data in a single view method.
    /// @return liquidity_ address of the Liquidity contract.
    /// @return lendingFactory_ address of the Lending factory contract.
    /// @return lendingRewardsRateModel_ address of the rewards rate model contract. changeable by LendingFactory auths.
    /// @return permit2_ address of the Permit2 contract used for deposits / mint with signature
    /// @return rebalancer_ address of the rebalancer allowed to execute `rebalance()`
    /// @return rewardsActive_ true if rewards are currently active
    /// @return liquidityBalance_ current Liquidity supply balance of `address(this)` for the underyling asset
    /// @return liquidityExchangePrice_ (updated) exchange price for the underlying assset in the liquidity protocol (without rewards)
    /// @return tokenExchangePrice_ (updated) exchange price between fToken and the underlying assset (with rewards)
    function getData()
        external
        view
        returns (
            IFluidLiquidity liquidity_,
            IFluidLendingFactory lendingFactory_,
            IFluidLendingRewardsRateModel lendingRewardsRateModel_,
            IAllowanceTransfer permit2_,
            address rebalancer_,
            bool rewardsActive_,
            uint256 liquidityBalance_,
            uint256 liquidityExchangePrice_,
            uint256 tokenExchangePrice_
        );

    /// @notice transfers `amount_` of `token_` to liquidity. Only callable by liquidity contract.
    /// @dev this callback is used to optimize gas consumption (reducing necessary token transfers).
    function liquidityCallback(address token_, uint256 amount_, bytes calldata data_) external;

    /// @notice deposit `assets_` amount with Permit2 signature for underlying asset approval.
    ///         reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached.
    ///         `assets_` must at least be `minDeposit()` amount; reverts otherwise.
    /// @param assets_ amount of assets to deposit
    /// @param receiver_ receiver of minted fToken shares
    /// @param minAmountOut_ minimum accepted amount of shares minted
    /// @param permit_ Permit2 permit message
    /// @param signature_  packed signature of signing the EIP712 hash of `permit_`
    /// @return shares_ amount of minted shares
    function depositWithSignature(
        uint256 assets_,
        address receiver_,
        uint256 minAmountOut_,
        IAllowanceTransfer.PermitSingle calldata permit_,
        bytes calldata signature_
    ) external returns (uint256 shares_);

    /// @notice mint amount of `shares_` with Permit2 signature for underlying asset approval.
    ///         Signature should approve a little bit more than expected assets amount (`previewMint()`) to avoid reverts.
    ///         `shares_` must at least be `minMint()` amount; reverts otherwise.
    ///         Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.
    ///         Recommended to use `deposit()` over mint because it is more gas efficient and less likely to revert.
    /// @param shares_ amount of shares to mint
    /// @param receiver_ receiver of minted fToken shares
    /// @param maxAssets_ maximum accepted amount of assets used as input to mint `shares_`
    /// @param permit_ Permit2 permit message
    /// @param signature_  packed signature of signing the EIP712 hash of `permit_`
    /// @return assets_ deposited assets amount
    function mintWithSignature(
        uint256 shares_,
        address receiver_,
        uint256 maxAssets_,
        IAllowanceTransfer.PermitSingle calldata permit_,
        bytes calldata signature_
    ) external returns (uint256 assets_);
}

interface IFTokenNativeUnderlying is IFToken {
    /// @notice address that is mapped to the chain native token at Liquidity
    function NATIVE_TOKEN_ADDRESS() external view returns (address);

    /// @notice deposits `msg.value` amount of native token for `receiver_`.
    ///         `msg.value` must be at least `minDeposit()` amount; reverts otherwise.
    ///         Recommended to use `depositNative()` with a `minAmountOut_` param instead to set acceptable limit.
    /// @return shares_ actually minted shares
    function depositNative(address receiver_) external payable returns (uint256 shares_);

    /// @notice same as {depositNative} but with an additional setting for minimum output amount.
    /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached
    function depositNative(address receiver_, uint256 minAmountOut_) external payable returns (uint256 shares_);

    /// @notice mints `shares_` for `receiver_`, paying with underlying native token.
    ///         `shares_` must at least be `minMint()` amount; reverts otherwise.
    ///         `shares_` set to type(uint256).max not supported.
    ///         Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.
    ///         Recommended to use `depositNative()` over mint because it is more gas efficient and less likely to revert.
    ///         Recommended to use `mintNative()` with a `minAmountOut_` param instead to set acceptable limit.
    /// @return assets_ deposited assets amount
    function mintNative(uint256 shares_, address receiver_) external payable returns (uint256 assets_);

    /// @notice same as {mintNative} but with an additional setting for minimum output amount.
    /// reverts with `fToken__MaxAmount()` if `maxAssets_` of assets is surpassed to mint `shares_`.
    function mintNative(
        uint256 shares_,
        address receiver_,
        uint256 maxAssets_
    ) external payable returns (uint256 assets_);

    /// @notice withdraws `assets_` amount in native underlying to `receiver_`, burning shares of `owner_`.
    ///         If `assets_` equals uint256.max then the whole fToken balance of `owner_` is withdrawn.This does not
    ///         consider withdrawal limit at liquidity so best to check with `maxWithdraw()` before.
    ///         Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.
    ///         Recommended to use `withdrawNative()` with a `maxSharesBurn_` param instead to set acceptable limit.
    /// @return shares_ burned shares
    function withdrawNative(uint256 assets_, address receiver_, address owner_) external returns (uint256 shares_);

    /// @notice same as {withdrawNative} but with an additional setting for minimum output amount.
    /// reverts with `fToken__MaxAmount()` if `maxSharesBurn_` of shares burned is surpassed.
    function withdrawNative(
        uint256 assets_,
        address receiver_,
        address owner_,
        uint256 maxSharesBurn_
    ) external returns (uint256 shares_);

    /// @notice redeems `shares_` to native underlying to `receiver_`, burning shares of `owner_`.
    ///         If `shares_` equals uint256.max then the whole balance of `owner_` is withdrawn.This does not
    ///         consider withdrawal limit at liquidity so best to check with `maxRedeem()` before.
    ///         Recommended to use `withdrawNative()` over redeem because it is more gas efficient and can set specific amount.
    ///         Recommended to use `redeemNative()` with a `minAmountOut_` param instead to set acceptable limit.
    /// @return assets_ withdrawn assets amount
    function redeemNative(uint256 shares_, address receiver_, address owner_) external returns (uint256 assets_);

    /// @notice same as {redeemNative} but with an additional setting for minimum output amount.
    /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of assets is not reached.
    function redeemNative(
        uint256 shares_,
        address receiver_,
        address owner_,
        uint256 minAmountOut_
    ) external returns (uint256 assets_);

    /// @notice withdraw amount of `assets_` in native token with ERC-2612 permit signature for fToken approval.
    /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.
    /// Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.
    /// allowance via signature should cover `previewWithdraw(assets_)` plus a little buffer to avoid revert.
    /// Inherent trust assumption that `msg.sender` will set `receiver_` and `minAmountOut_` as `owner_` intends
    /// (which is always the case when giving allowance to some spender).
    /// @param sharesToPermit_ shares amount to use for EIP2612 permit(). Should cover `previewWithdraw(assets_)` + small buffer.
    /// @param assets_ amount of assets to withdraw
    /// @param receiver_ receiver of withdrawn assets
    /// @param owner_ owner to withdraw from (must be signature signer)
    /// @param maxSharesBurn_ maximum accepted amount of shares burned
    /// @param deadline_ deadline for signature validity
    /// @param signature_  packed signature of signing the EIP712 hash for ERC-2612 permit
    /// @return shares_ burned shares amount
    function withdrawWithSignatureNative(
        uint256 sharesToPermit_,
        uint256 assets_,
        address receiver_,
        address owner_,
        uint256 maxSharesBurn_,
        uint256 deadline_,
        bytes calldata signature_
    ) external returns (uint256 shares_);

    /// @notice redeem amount of `shares_` as native token with ERC-2612 permit signature for fToken approval.
    /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.
    /// Note there might be tiny inaccuracies between requested `shares_` to redeem and actually burned shares.
    /// allowance via signature must cover `shares_` plus a tiny buffer.
    /// Inherent trust assumption that `msg.sender` will set `receiver_` and `minAmountOut_` as `owner_` intends
    ///       (which is always the case when giving allowance to some spender).
    /// Recommended to use `withdrawNative()` over redeem because it is more gas efficient and can set specific amount.
    /// @param shares_ amount of shares to redeem
    /// @param receiver_ receiver of withdrawn assets
    /// @param owner_ owner to withdraw from (must be signature signer)
    /// @param minAmountOut_ minimum accepted amount of assets withdrawn
    /// @param deadline_ deadline for signature validity
    /// @param signature_  packed signature of signing the EIP712 hash for ERC-2612 permit
    /// @return assets_ withdrawn assets amount
    function redeemWithSignatureNative(
        uint256 shares_,
        address receiver_,
        address owner_,
        uint256 minAmountOut_,
        uint256 deadline_,
        bytes calldata signature_
    ) external returns (uint256 assets_);
}

File 24 of 33 : iLendingFactory.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { IFluidLiquidity } from "../../../liquidity/interfaces/iLiquidity.sol";

interface IFluidLendingFactoryAdmin {
    /// @notice reads if a certain `auth_` address is an allowed auth or not. Owner is auth by default.
    function isAuth(address auth_) external view returns (bool);

    /// @notice              Sets an address as allowed auth or not. Only callable by owner.
    /// @param auth_         address to set auth value for
    /// @param allowed_      bool flag for whether address is allowed as auth or not
    function setAuth(address auth_, bool allowed_) external;

    /// @notice reads if a certain `deployer_` address is an allowed deployer or not. Owner is deployer by default.
    function isDeployer(address deployer_) external view returns (bool);

    /// @notice              Sets an address as allowed deployer or not. Only callable by owner.
    /// @param deployer_     address to set deployer value for
    /// @param allowed_      bool flag for whether address is allowed as deployer or not
    function setDeployer(address deployer_, bool allowed_) external;

    /// @notice              Sets the `creationCode_` bytecode for a certain `fTokenType_`. Only callable by auths.
    /// @param fTokenType_   the fToken Type used to refer the creation code
    /// @param creationCode_ contract creation code. can be set to bytes(0) to remove a previously available `fTokenType_`
    function setFTokenCreationCode(string memory fTokenType_, bytes calldata creationCode_) external;

    /// @notice creates token for `asset_` for a lending protocol with interest. Only callable by deployers.
    /// @param  asset_              address of the asset
    /// @param  fTokenType_         type of fToken:
    /// - if it's the native token, it should use `NativeUnderlying`
    /// - otherwise it should use `fToken`
    /// - could be more types available, check `fTokenTypes()`
    /// @param  isNativeUnderlying_ flag to signal fToken type that uses native underlying at Liquidity
    /// @return token_              address of the created token
    function createToken(
        address asset_,
        string calldata fTokenType_,
        bool isNativeUnderlying_
    ) external returns (address token_);
}

interface IFluidLendingFactory is IFluidLendingFactoryAdmin {
    /// @notice list of all created tokens
    function allTokens() external view returns (address[] memory);

    /// @notice list of all fToken types that can be deployed
    function fTokenTypes() external view returns (string[] memory);

    /// @notice returns the creation code for a certain `fTokenType_`
    function fTokenCreationCode(string memory fTokenType_) external view returns (bytes memory);

    /// @notice address of the Liquidity contract.
    function LIQUIDITY() external view returns (IFluidLiquidity);

    /// @notice computes deterministic token address for `asset_` for a lending protocol
    /// @param  asset_      address of the asset
    /// @param  fTokenType_         type of fToken:
    /// - if it's the native token, it should use `NativeUnderlying`
    /// - otherwise it should use `fToken`
    /// - could be more types available, check `fTokenTypes()`
    /// @return token_      detemrinistic address of the computed token
    function computeToken(address asset_, string calldata fTokenType_) external view returns (address token_);
}

File 25 of 33 : iLendingRewardsRateModel.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IFluidLendingRewardsRateModel {
    /// @notice Calculates the current rewards rate (APR)
    /// @param totalAssets_ amount of assets in the lending
    /// @return rate_ rewards rate percentage per year with 1e12 RATE_PRECISION, e.g. 1e12 = 1%, 1e14 = 100%
    /// @return ended_ flag to signal that rewards have ended (always 0 going forward)
    /// @return startTime_ start time of rewards to compare against last update timestamp
    function getRate(uint256 totalAssets_) external view returns (uint256 rate_, bool ended_, uint256 startTime_);

    /// @notice Returns config constants for rewards rate model
    function getConfig()
        external
        view
        returns (
            uint256 duration_,
            uint256 startTime_,
            uint256 endTime_,
            uint256 startTvl_,
            uint256 maxRate_,
            uint256 rewardAmount_,
            address initiator_
        );
}

File 26 of 33 : iAllowanceTransfer.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
/// from https://github.com/Uniswap/permit2/blob/main/src/interfaces/ISignatureTransfer.sol.
/// Copyright (c) 2022 Uniswap Labs
interface IAllowanceTransfer {
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice Thrown when an allowance on a token has expired.
    /// @param deadline The timestamp at which the allowed amount is no longer valid
    error AllowanceExpired(uint256 deadline);

    /// @notice Thrown when an allowance on a token has been depleted.
    /// @param amount The maximum amount allowed
    error InsufficientAllowance(uint256 amount);

    /// @notice Thrown when too many nonces are invalidated.
    error ExcessiveInvalidation();

    /// @notice Emits an event when the owner successfully invalidates an ordered nonce.
    event NonceInvalidation(
        address indexed owner,
        address indexed token,
        address indexed spender,
        uint48 newNonce,
        uint48 oldNonce
    );

    /// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
    event Approval(
        address indexed owner,
        address indexed token,
        address indexed spender,
        uint160 amount,
        uint48 expiration
    );

    /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
    event Permit(
        address indexed owner,
        address indexed token,
        address indexed spender,
        uint160 amount,
        uint48 expiration,
        uint48 nonce
    );

    /// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
    event Lockdown(address indexed owner, address token, address spender);

    /// @notice The permit data for a token
    struct PermitDetails {
        // ERC20 token address
        address token;
        // the maximum amount allowed to spend
        uint160 amount;
        // timestamp at which a spender's token allowances become invalid
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each signature
        uint48 nonce;
    }

    /// @notice The permit message signed for a single token allownce
    struct PermitSingle {
        // the permit data for a single token alownce
        PermitDetails details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }

    /// @notice The permit message signed for multiple token allowances
    struct PermitBatch {
        // the permit data for multiple token allowances
        PermitDetails[] details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }

    /// @notice The saved permissions
    /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
    /// @dev Setting amount to type(uint160).max sets an unlimited approval
    struct PackedAllowance {
        // amount allowed
        uint160 amount;
        // permission expiry
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each signature
        uint48 nonce;
    }

    /// @notice A token spender pair.
    struct TokenSpenderPair {
        // the token the spender is approved
        address token;
        // the spender address
        address spender;
    }

    /// @notice Details for a token transfer.
    struct AllowanceTransferDetails {
        // the owner of the token
        address from;
        // the recipient of the token
        address to;
        // the amount of the token
        uint160 amount;
        // the token to be transferred
        address token;
    }

    /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
    /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
    /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
    function allowance(
        address user,
        address token,
        address spender
    ) external view returns (uint160 amount, uint48 expiration, uint48 nonce);

    /// @notice Approves the spender to use up to amount of the specified token up until the expiration
    /// @param token The token to approve
    /// @param spender The spender address to approve
    /// @param amount The approved amount of the token
    /// @param expiration The timestamp at which the approval is no longer valid
    /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
    /// @dev Setting amount to type(uint160).max sets an unlimited approval
    function approve(address token, address spender, uint160 amount, uint48 expiration) external;

    /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
    /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
    /// @param owner The owner of the tokens being approved
    /// @param permitSingle Data signed over by the owner specifying the terms of approval
    /// @param signature The owner's signature over the permit data
    function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;

    /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
    /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
    /// @param owner The owner of the tokens being approved
    /// @param permitBatch Data signed over by the owner specifying the terms of approval
    /// @param signature The owner's signature over the permit data
    function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;

    /// @notice Transfer approved tokens from one address to another
    /// @param from The address to transfer from
    /// @param to The address of the recipient
    /// @param amount The amount of the token to transfer
    /// @param token The token address to transfer
    /// @dev Requires the from address to have approved at least the desired amount
    /// of tokens to msg.sender.
    function transferFrom(address from, address to, uint160 amount, address token) external;

    /// @notice Transfer approved tokens in a batch
    /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
    /// @dev Requires the from addresses to have approved at least the desired amount
    /// of tokens to msg.sender.
    function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;

    /// @notice Enables performing a "lockdown" of the sender's Permit2 identity
    /// by batch revoking approvals
    /// @param approvals Array of approvals to revoke.
    function lockdown(TokenSpenderPair[] calldata approvals) external;

    /// @notice Invalidate nonces for a given (token, spender) pair
    /// @param token The token to invalidate nonces for
    /// @param spender The spender to invalidate nonces for
    /// @param newNonce The new nonce to set. Invalidates all nonces less than it.
    /// @dev Can't invalidate more than 2**16 nonces per transaction.
    function invalidateNonces(address token, address spender, uint48 newNonce) external;
}

File 27 of 33 : iVault.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

/// @notice common Fluid vaults interface, some methods only available for vaults > T1 (type, simulateLiquidate, rebalance is different)
interface IFluidVault {
    /// @notice returns the vault id
    function VAULT_ID() external view returns (uint256);

    /// @notice returns the vault id
    function TYPE() external view returns (uint256);

    /// @notice reads uint256 data `result_` from storage at a bytes32 storage `slot_` key.
    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);

    struct Tokens {
        address token0;
        address token1;
    }

    struct ConstantViews {
        address liquidity;
        address factory;
        address operateImplementation;
        address adminImplementation;
        address secondaryImplementation;
        address deployer; // address which deploys oracle
        address supply; // either liquidity layer or DEX protocol
        address borrow; // either liquidity layer or DEX protocol
        Tokens supplyToken; // if smart collateral then address of token0 & token1 else just supply token address at token0 and token1 as empty
        Tokens borrowToken; // if smart debt then address of token0 & token1 else just borrow token address at token0 and token1 as empty
        uint256 vaultId;
        uint256 vaultType;
        bytes32 supplyExchangePriceSlot; // if smart collateral then slot is from DEX protocol else from liquidity layer
        bytes32 borrowExchangePriceSlot; // if smart debt then slot is from DEX protocol else from liquidity layer
        bytes32 userSupplySlot; // if smart collateral then slot is from DEX protocol else from liquidity layer
        bytes32 userBorrowSlot; // if smart debt then slot is from DEX protocol else from liquidity layer
    }

    /// @notice returns all Vault constants
    function constantsView() external view returns (ConstantViews memory constantsView_);

    /// @notice fetches the latest user position after a liquidation
    function fetchLatestPosition(
        int256 positionTick_,
        uint256 positionTickId_,
        uint256 positionRawDebt_,
        uint256 tickData_
    )
        external
        view
        returns (
            int256, // tick
            uint256, // raw debt
            uint256, // raw collateral
            uint256, // branchID_
            uint256 // branchData_
        );

    /// @notice calculates the updated vault exchange prices
    function updateExchangePrices(
        uint256 vaultVariables2_
    )
        external
        view
        returns (
            uint256 liqSupplyExPrice_,
            uint256 liqBorrowExPrice_,
            uint256 vaultSupplyExPrice_,
            uint256 vaultBorrowExPrice_
        );

    /// @notice calculates the updated vault exchange prices and writes them to storage
    function updateExchangePricesOnStorage()
        external
        returns (
            uint256 liqSupplyExPrice_,
            uint256 liqBorrowExPrice_,
            uint256 vaultSupplyExPrice_,
            uint256 vaultBorrowExPrice_
        );

    /// @notice returns the liquidity contract address
    function LIQUIDITY() external view returns (address);

    error FluidLiquidateResult(uint256 colLiquidated, uint256 debtLiquidated);

    function rebalance(
        int colToken0MinMax_,
        int colToken1MinMax_,
        int debtToken0MinMax_,
        int debtToken1MinMax_
    ) external payable returns (int supplyAmt_, int borrowAmt_);

    /// @notice reverts with FluidLiquidateResult
    function simulateLiquidate(uint debtAmt_, bool absorb_) external;
}

File 28 of 33 : iVaultT1.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IFluidVaultT1 {
    /// @notice returns the vault id
    function VAULT_ID() external view returns (uint256);

    /// @notice reads uint256 data `result_` from storage at a bytes32 storage `slot_` key.
    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);

    struct ConstantViews {
        address liquidity;
        address factory;
        address adminImplementation;
        address secondaryImplementation;
        address supplyToken;
        address borrowToken;
        uint8 supplyDecimals;
        uint8 borrowDecimals;
        uint vaultId;
        bytes32 liquiditySupplyExchangePriceSlot;
        bytes32 liquidityBorrowExchangePriceSlot;
        bytes32 liquidityUserSupplySlot;
        bytes32 liquidityUserBorrowSlot;
    }

    /// @notice returns all Vault constants
    function constantsView() external view returns (ConstantViews memory constantsView_);

    /// @notice fetches the latest user position after a liquidation
    function fetchLatestPosition(
        int256 positionTick_,
        uint256 positionTickId_,
        uint256 positionRawDebt_,
        uint256 tickData_
    )
        external
        view
        returns (
            int256, // tick
            uint256, // raw debt
            uint256, // raw collateral
            uint256, // branchID_
            uint256 // branchData_
        );

    /// @notice calculates the updated vault exchange prices
    function updateExchangePrices(
        uint256 vaultVariables2_
    )
        external
        view
        returns (
            uint256 liqSupplyExPrice_,
            uint256 liqBorrowExPrice_,
            uint256 vaultSupplyExPrice_,
            uint256 vaultBorrowExPrice_
        );

    /// @notice calculates the updated vault exchange prices and writes them to storage
    function updateExchangePricesOnStorage()
        external
        returns (
            uint256 liqSupplyExPrice_,
            uint256 liqBorrowExPrice_,
            uint256 vaultSupplyExPrice_,
            uint256 vaultBorrowExPrice_
        );

    /// @notice returns the liquidity contract address
    function LIQUIDITY() external view returns (address);

    function operate(
        uint256 nftId_, // if 0 then new position
        int256 newCol_, // if negative then withdraw
        int256 newDebt_, // if negative then payback
        address to_ // address at which the borrow & withdraw amount should go to. If address(0) then it'll go to msg.sender
    )
        external
        payable
        returns (
            uint256, // nftId_
            int256, // final supply amount. if - then withdraw
            int256 // final borrow amount. if - then payback
        );

    function liquidate(
        uint256 debtAmt_,
        uint256 colPerUnitDebt_, // min collateral needed per unit of debt in 1e18
        address to_,
        bool absorb_
    ) external payable returns (uint actualDebtAmt_, uint actualColAmt_);

    function absorb() external;

    function rebalance() external payable returns (int supplyAmt_, int borrowAmt_);

    error FluidLiquidateResult(uint256 colLiquidated, uint256 debtLiquidated);
}

File 29 of 33 : error.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Error {
    error FluidReserveContractError(uint256 errorId_);
}

File 30 of 33 : errorTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

library ErrorTypes {
    /***********************************|
    |               Reserve             | 
    |__________________________________*/

    /// @notice thrown when an unauthorized caller is trying to execute an auth-protected method
    uint256 internal constant ReserveContract__Unauthorized = 90001;

    /// @notice thrown when an input address is zero
    uint256 internal constant ReserveContract__AddressZero = 90002;

    /// @notice thrown when input arrays has different lenghts
    uint256 internal constant ReserveContract__InvalidInputLenghts = 90003;

    /// @notice thrown when renounceOwnership is called
    uint256 internal constant ReserveContract__RenounceOwnershipUnsupported = 90004;

    /// @notice thrown when wrong msg.value is at time of rebalancing
    uint256 internal constant ReserveContract__WrongValueSent = 90005;

    /// @notice thrown when there is insufficient allowance to a protocol
    uint256 internal constant ReserveContract__InsufficientAllowance = 90006;
}

File 31 of 33 : events.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Events {
    /// @notice Emitted when an address is added or removed from the auths
    event LogUpdateAuth(address indexed auth, bool isAuth);

    /// @notice Emitted when an address is added or removed from the rebalancers
    event LogUpdateRebalancer(address indexed rebalancer, bool isRebalancer);

    /// @notice Emitted when a token is approved for use by a protocol
    event LogAllow(address indexed protocol, address indexed token, uint256 newAllowance, uint existingAllowance);

    /// @notice Emitted when a token is revoked for use by a protocol
    event LogRevoke(address indexed protocol, address indexed token);

    /// @notice Emitted when fToken is rebalanced
    event LogRebalanceFToken(address indexed protocol, uint amount);

    /// @notice Emitted when vault is rebalanced
    event LogRebalanceVault(address indexed protocol, int colAmount, int debtAmount);

    /// @notice Emitted whenever funds for a certain `token` are transfered to Liquidity
    event LogTransferFunds(address indexed token);

    /// @notice Emitted whenever funds for a certain `token` are withdrawn to receiver
    event LogWithdrawFunds(address indexed token, uint256 indexed amount, address receiver);
}

File 32 of 33 : structs.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

abstract contract Structs {
    struct TokenAllowance {
        address token;
        uint256 allowance;
    }

    struct ProtocolTokenAllowance {
        address protocol;
        TokenAllowance[] tokenAllowances;
    }
}

File 33 of 33 : variables.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { Structs } from "./structs.sol";

abstract contract Constants {
    /// @dev address that is mapped to the chain native token
    address internal constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
}

abstract contract Variables is Constants, Initializable, OwnableUpgradeable, Structs {
    using EnumerableSet for EnumerableSet.AddressSet;

    // ------------ storage variables from inherited contracts (Initializable, OwnableUpgradeable) come before vars here --------
    // @dev variables here start at storage slot 101, before is:
    // - Initializable with storage slot 0:
    // uint8 private _initialized;
    // bool private _initializing;
    // - OwnableUpgradeable with slots 1 to 100:
    // uint256[50] private __gap; (from ContextUpgradeable, slot 1 until slot 50)
    // address private _owner; (at slot 51)
    // uint256[49] private __gap; (slot 52 until slot 100)

    // ----------------------- slot 101 ---------------------------
    /// @notice Maps address to there status as an Auth
    mapping(address => bool) public isAuth;

    /// @notice Maps address to there status as a Rebalancer
    mapping(address => bool) public isRebalancer;

    /// @notice Mapping of protocol addresses to the tokens that are allowed to be used by that protocol
    mapping(address => EnumerableSet.AddressSet) internal _protocolTokens;

    /// @notice Mapping of protocol addresses to the amount of tokens that are allowed to be used by that protocol
    mapping(address => uint256) public nativeTokenAllowances;

    /// @notice Array of all the protocols that had tokens allowed to be used
    EnumerableSet.AddressSet internal _protocols;
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidReserveContractError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidSafeTransferError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"protocol","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newAllowance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"existingAllowance","type":"uint256"}],"name":"LogAllow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"protocol","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LogRebalanceFToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"protocol","type":"address"},{"indexed":false,"internalType":"int256","name":"colAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"debtAmount","type":"int256"}],"name":"LogRebalanceVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"protocol","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"LogRevoke","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"LogTransferFunds","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"auth","type":"address"},{"indexed":false,"internalType":"bool","name":"isAuth","type":"bool"}],"name":"LogUpdateAuth","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rebalancer","type":"address"},{"indexed":false,"internalType":"bool","name":"isRebalancer","type":"bool"}],"name":"LogUpdateRebalancer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"LogWithdrawFunds","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[{"internalType":"address[]","name":"protocols_","type":"address[]"},{"internalType":"address[]","name":"tokens_","type":"address[]"},{"internalType":"uint256[]","name":"amounts_","type":"uint256[]"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllProtocolAllowances","outputs":[{"components":[{"internalType":"address","name":"protocol","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"}],"internalType":"struct Structs.TokenAllowance[]","name":"tokenAllowances","type":"tuple[]"}],"internalType":"struct Structs.ProtocolTokenAllowance[]","name":"allowances_","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"protocol_","type":"address"}],"name":"getProtocolAllowances","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"}],"internalType":"struct Structs.TokenAllowance[]","name":"allowances_","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"protocol_","type":"address"}],"name":"getProtocolTokens","outputs":[{"internalType":"address[]","name":"result_","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_auths","type":"address[]"},{"internalType":"address[]","name":"_rebalancers","type":"address[]"},{"internalType":"address","name":"owner_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isAuth","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isRebalancer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nativeTokenAllowances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"protocol_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"int256","name":"colToken0MinMax_","type":"int256"},{"internalType":"int256","name":"colToken1MinMax_","type":"int256"},{"internalType":"int256","name":"debtToken0MinMax_","type":"int256"},{"internalType":"int256","name":"debtToken1MinMax_","type":"int256"}],"name":"rebalanceDexVault","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"protocols_","type":"address[]"},{"internalType":"uint256[]","name":"values_","type":"uint256[]"},{"internalType":"int256[]","name":"colToken0MinMaxs_","type":"int256[]"},{"internalType":"int256[]","name":"colToken1MinMaxs_","type":"int256[]"},{"internalType":"int256[]","name":"debtToken0MinMaxs_","type":"int256[]"},{"internalType":"int256[]","name":"debtToken1MinMaxs_","type":"int256[]"}],"name":"rebalanceDexVaults","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"protocol_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"}],"name":"rebalanceFToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"protocols_","type":"address[]"},{"internalType":"uint256[]","name":"values_","type":"uint256[]"}],"name":"rebalanceFTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"protocol_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"}],"name":"rebalanceVault","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"protocols_","type":"address[]"},{"internalType":"uint256[]","name":"values_","type":"uint256[]"}],"name":"rebalanceVaults","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"protocols_","type":"address[]"},{"internalType":"address[]","name":"tokens_","type":"address[]"}],"name":"revoke","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"auth_","type":"address"},{"internalType":"bool","name":"isAuth_","type":"bool"}],"name":"updateAuth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rebalancer_","type":"address"},{"internalType":"bool","name":"isRebalancer_","type":"bool"}],"name":"updateRebalancer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens_","type":"address[]"},{"internalType":"uint256[]","name":"amounts_","type":"uint256[]"},{"internalType":"address","name":"receiver_","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a0604052306080523480156200001557600080fd5b506200002062000026565b620000e8565b600054610100900460ff1615620000935760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161015620000e6576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b608051613f396200012060003960008181610fa001528181611050015281816117bd0152818161186d0152611d970152613f396000f3fe60806040526004361061018f5760003560e01c80634f1ef286116100d65780638da5cb5b1161007f578063ec1a9dd411610059578063ec1a9dd41461043e578063ef33e9db1461046b578063f2fde38b1461048d57600080fd5b80638da5cb5b146103bc578063c11b353c146103f1578063e9c771b21461041e57600080fd5b8063702b5175116100b0578063702b51751461035a578063715018a61461037a578063801ca1441461038f57600080fd5b80634f1ef286146103045780635274ac3f1461031757806352d1902d1461033757600080fd5b80633659cfe611610138578063432abd5611610112578063432abd56146102a1578063467c9eff146102b45780634c13c1be146102e457600080fd5b80633659cfe61461025b578063386b8e6f1461027b578063392094fc1461028e57600080fd5b806313b14ebb1161016957806313b14ebb146101f05780632520e7ff146102035780632bb393991461024857600080fd5b806303e804611461019b5780630de30836146101bd578063111a399b146101dd57600080fd5b3661019657005b600080fd5b3480156101a757600080fd5b506101bb6101b6366004613546565b6104ad565b005b3480156101c957600080fd5b506101bb6101d83660046135dc565b6107e9565b6101bb6101eb366004613615565b610958565b6101bb6101fe366004613615565b610d2e565b34801561020f57600080fd5b5061023361021e366004613641565b60656020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6101bb6102563660046136b1565b610edf565b34801561026757600080fd5b506101bb610276366004613641565b610f89565b6101bb61028936600461371d565b61118e565b6101bb61029c3660046136b1565b6113f7565b6101bb6102af366004613769565b61149a565b3480156102c057600080fd5b506102336102cf366004613641565b60666020526000908152604090205460ff1681565b3480156102f057600080fd5b506101bb6102ff3660046138ac565b6115de565b6101bb610312366004613924565b6117a6565b34801561032357600080fd5b506101bb6103323660046139ea565b61199c565b34801561034357600080fd5b5061034c611d7d565b60405190815260200161023f565b34801561036657600080fd5b506101bb610375366004613a46565b611e69565b34801561038657600080fd5b506101bb6120e5565b34801561039b57600080fd5b506103af6103aa366004613641565b612124565b60405161023f9190613aaa565b3480156103c857600080fd5b5060335460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161023f565b3480156103fd57600080fd5b5061034c61040c366004613641565b60686020526000908152604090205481565b34801561042a57600080fd5b506101bb6104393660046135dc565b612206565b34801561044a57600080fd5b5061045e610459366004613641565b6122e4565b60405161023f9190613b60565b34801561047757600080fd5b50610480612511565b60405161023f9190613b73565b34801561049957600080fd5b506101bb6104a8366004613641565b61261b565b3360009081526065602052604090205460ff161580156105015750336104e860335473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610542576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f9160048201526024015b60405180910390fd5b8151835114158061055557508051825114155b15610591576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b83518110156107e35760008482815181106105b1576105b1613c1b565b6020026020010151905060008483815181106105cf576105cf613c1b565b6020026020010151905060008484815181106105ed576105ed613c1b565b60200260200101519050600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610671575073ffffffffffffffffffffffffffffffffffffffff83166000908152606860205260409020805490829055610720565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff858116602483015284169063dd62ed3e90604401602060405180830381865afa1580156106e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107079190613c4a565b9050610715838560006126cf565b6107208385846126cf565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260676020526040902061074f9084612888565b5061075b606985612888565b508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167ff07829fca7213317a8d0869b30e579887a8d2a7bfd3a28f25075bd01b370d75684846040516107c4929190918252602082015260400190565b60405180910390a35050505080806107db90613c92565b915050610594565b50505050565b3360009081526065602052604090205460ff1615801561083d57503361082460335473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610879576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b8173ffffffffffffffffffffffffffffffffffffffff81166108cc576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f926004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff831660008181526066602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527fad476fc62f6b1b5d25b35bd756cfbfcd299b581b8dfb25d5492c4305a0969bd291015b60405180910390a2505050565b3360009081526066602052604090205460ff166109a6576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b8015610a4b5773ffffffffffffffffffffffffffffffffffffffff8216600090815260686020526040902054811115610a10576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f966004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff821660009081526068602052604081208054839290610a45908490613cca565b90915550505b6000808373ffffffffffffffffffffffffffffffffffffffff16637d7c2a1c846040518263ffffffff1660e01b8152600401604080518083038185885af1158015610a9a573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610abf9190613cdd565b90925090508215610cd75760008473ffffffffffffffffffffffffffffffffffffffff1663b7791bf26040518163ffffffff1660e01b81526004016101a060405180830381865afa158015610b18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3c9190613d22565b608081015190915073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148015610b7c5750600083125b15610bb8576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f956004820152602401610539565b60a081015173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148015610bf55750600082135b15610c31576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f956004820152602401610539565b608081015173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480610c9a575060a081015173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b610cd5576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f956004820152602401610539565b505b604080518381526020810183905273ffffffffffffffffffffffffffffffffffffffff8616917f1b5a6c038413ee85baaa108b9e0f353fc1fb6dba5c500211baef62b0761e4d88910160405180910390a250505050565b3360009081526066602052604090205460ff16610d7c576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b8015610e215773ffffffffffffffffffffffffffffffffffffffff8216600090815260686020526040902054811115610de6576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f966004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff821660009081526068602052604081208054839290610e1b908490613cca565b90915550505b60008273ffffffffffffffffffffffffffffffffffffffff16637d7c2a1c836040518263ffffffff1660e01b815260040160206040518083038185885af1158015610e70573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610e959190613c4a565b90508273ffffffffffffffffffffffffffffffffffffffff167f2ce777e59f0be485fcc70a4790aa17e9aca8e9e6704e6ab690fc2018c92cca3f8260405161094b91815260200190565b828114610f1d576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b83811015610f8257610f70858583818110610f3d57610f3d613c1b565b9050602002016020810190610f529190613641565b848484818110610f6457610f64613c1b565b90506020020135610958565b80610f7a81613c92565b915050610f20565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016300361104e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610539565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166110c37f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611166576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610539565b61116f816128b3565b6040805160008082526020820190925261118b918391906128bb565b50565b3360009081526066602052604090205460ff166111dc576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b478515801590611210575073ffffffffffffffffffffffffffffffffffffffff871660009081526068602052604090205486115b1561124c576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f966004820152602401610539565b6040517f1593a34b00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044810184905260648101839052600090819073ffffffffffffffffffffffffffffffffffffffff8a1690631593a34b908a90608401604080518083038185885af11580156112d3573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906112f89190613cdd565b91509150600088118015611316575060008213806113165750600081125b1561139b57600047841161132b576000611335565b6113354785613cca565b905034156113585734811161134b576000611355565b6113553482613cca565b90505b80156113995773ffffffffffffffffffffffffffffffffffffffff8a1660009081526068602052604081208054839290611393908490613cca565b90915550505b505b604080518381526020810183905273ffffffffffffffffffffffffffffffffffffffff8b16917f1b5a6c038413ee85baaa108b9e0f353fc1fb6dba5c500211baef62b0761e4d88910160405180910390a2505050505050505050565b828114611435576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b83811015610f825761148885858381811061145557611455613c1b565b905060200201602081019061146a9190613641565b84848481811061147c5761147c613c1b565b90506020020135610d2e565b8061149281613c92565b915050611438565b8a891415806114a957508a8714155b806114b457508a8514155b806114bf57508a8314155b806114ca57508a8114155b15611506576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b8b8110156115cf576115bd8d8d8381811061152657611526613c1b565b905060200201602081019061153b9190613641565b8c8c8481811061154d5761154d613c1b565b905060200201358b8b8581811061156657611566613c1b565b905060200201358a8a8681811061157f5761157f613c1b565b9050602002013589898781811061159857611598613c1b565b905060200201358888888181106115b1576115b1613c1b565b9050602002013561118e565b806115c781613c92565b915050611509565b50505050505050505050505050565b6115e6612aba565b8151835114611626576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b83518110156107e35773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff1684828151811061166f5761166f613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16036116ba576116b5828483815181106116a8576116a8613c1b565b6020026020010151612b3d565b6116f7565b6116f78482815181106116cf576116cf613c1b565b6020026020010151838584815181106116ea576116ea613c1b565b6020026020010151612b89565b82818151811061170957611709613c1b565b602002602001015184828151811061172357611723613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fed037dbd552690e525e4da3c8b4ea3e5f707bfccda2ea6bb6647480383b52cdd8460405161178c919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b60405180910390a38061179e81613c92565b915050611629565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016300361186b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610539565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166118e07f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611983576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610539565b61198c826128b3565b611998828260016128bb565b5050565b600054610100900460ff16158080156119bc5750600054600160ff909116105b806119d65750303b1580156119d6575060005460ff166001145b611a62576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610539565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611ac057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b8173ffffffffffffffffffffffffffffffffffffffff8116611b13576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f926004820152602401610539565b60005b8551811015611c0e57600160656000888481518110611b3757611b37613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550858181518110611ba257611ba2613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fb873643b3104ddd26927dec7f9a08aa22d03ac20ada0295a2de7e1a6c60f2a516001604051611bf4911515815260200190565b60405180910390a280611c0681613c92565b915050611b16565b5060005b8451811015611d0a57600160666000878481518110611c3357611c33613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550848181518110611c9e57611c9e613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fad476fc62f6b1b5d25b35bd756cfbfcd299b581b8dfb25d5492c4305a0969bd26001604051611cf0911515815260200190565b60405180910390a280611d0281613c92565b915050611c12565b50611d1483612c2d565b5080156107e357600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050565b60003073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611e44576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c00000000000000006064820152608401610539565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b3360009081526065602052604090205460ff16158015611ebd575033611ea460335473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614155b15611ef9576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b8051825114611f39576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b82518110156120e0576000838281518110611f5957611f59613c1b565b602002602001015190506000838381518110611f7757611f77613c1b565b6020026020010151905073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611ff35773ffffffffffffffffffffffffffffffffffffffff8216600090815260686020526040812055611fff565b611fff818360006126cf565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260676020526040902061202e9082612ca4565b5073ffffffffffffffffffffffffffffffffffffffff8216600090815260676020526040902061205d90612cc6565b6000036120715761206f606983612ca4565b505b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fd54cf739d89b84837b7ca6e1402410c3497b29b0a5fbe452e57008b3abd8874d60405160405180910390a3505080806120d890613c92565b915050611f3c565b505050565b6120ed612aba565b6040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f946004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260676020526040902060609061215581612cc6565b67ffffffffffffffff81111561216d5761216d613389565b604051908082528060200260200182016040528015612196578160200160208202803683370190505b50915060005b6121a582612cc6565b8110156121ff576121b68282612cd0565b8382815181106121c8576121c8613c1b565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152806121f781613c92565b91505061219c565b5050919050565b61220e612aba565b8173ffffffffffffffffffffffffffffffffffffffff8116612261576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f926004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff831660008181526065602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527fb873643b3104ddd26927dec7f9a08aa22d03ac20ada0295a2de7e1a6c60f2a51910161094b565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260676020526040902060609061231581612cc6565b67ffffffffffffffff81111561232d5761232d613389565b60405190808252806020026020018201604052801561237257816020015b604080518082019091526000808252602082015281526020019060019003908161234b5790505b50915060005b61238182612cc6565b8110156121ff5760006123948383612cd0565b9050808483815181106123a9576123a9613c1b565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff918216905281167fffffffffffffffffffffffff1111111111111111111111111111111111111112016124455773ffffffffffffffffffffffffffffffffffffffff8516600090815260686020526040902054845185908490811061243057612430613c1b565b602002602001015160200181815250506124fe565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff868116602483015282169063dd62ed3e90604401602060405180830381865afa1580156124b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124db9190613c4a565b8483815181106124ed576124ed613c1b565b602002602001015160200181815250505b508061250981613c92565b915050612378565b606061251d6069612cc6565b67ffffffffffffffff81111561253557612535613389565b60405190808252806020026020018201604052801561257b57816020015b6040805180820190915260008152606060208201528152602001906001900390816125535790505b50905060005b61258b6069612cc6565b81101561261757600061259f606983612cd0565b9050808383815181106125b4576125b4613c1b565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff90911690526125e2816122e4565b8383815181106125f4576125f4613c1b565b60200260200101516020018190525050808061260f90613c92565b915050612581565b5090565b612623612aba565b73ffffffffffffffffffffffffffffffffffffffff81166126c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610539565b61118b81612c2d565b80158061276f57506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612749573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061276d9190613c4a565b155b6127fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610539565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b3000000000000000000000000000000000000000000000000000000001790526120e0908490612cdc565b60006128aa8373ffffffffffffffffffffffffffffffffffffffff8416612de8565b90505b92915050565b61118b612aba565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff16156128ee576120e083612e37565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612973575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261297091810190613c4a565b60015b6129ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f7420555550530000000000000000000000000000000000006064820152608401610539565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8114612aae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c655555494400000000000000000000000000000000000000000000006064820152608401610539565b506120e0838383612f41565b60335473ffffffffffffffffffffffffffffffffffffffff163314612b3b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610539565b565b60008060008060008587614e20f19050806120e0576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a6004820152602401610539565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806107e3576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a6004820152602401610539565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006128aa8373ffffffffffffffffffffffffffffffffffffffff8416612f66565b60006128ad825490565b60006128aa8383613059565b6000612d3e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166130839092919063ffffffff16565b8051909150156120e05780806020019051810190612d5c9190613dff565b6120e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610539565b6000818152600183016020526040812054612e2f575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556128ad565b5060006128ad565b73ffffffffffffffffffffffffffffffffffffffff81163b612edb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e7472616374000000000000000000000000000000000000006064820152608401610539565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612f4a8361309a565b600082511180612f575750805b156120e0576107e383836130e7565b6000818152600183016020526040812054801561304f576000612f8a600183613cca565b8554909150600090612f9e90600190613cca565b9050818114613003576000866000018281548110612fbe57612fbe613c1b565b9060005260206000200154905080876000018481548110612fe157612fe1613c1b565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061301457613014613e1c565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506128ad565b60009150506128ad565b600082600001828154811061307057613070613c1b565b9060005260206000200154905092915050565b6060613092848460008561310c565b949350505050565b6130a381612e37565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606128aa8383604051806060016040528060278152602001613edd60279139613225565b60608247101561319e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610539565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516131c79190613e6f565b60006040518083038185875af1925050503d8060008114613204576040519150601f19603f3d011682016040523d82523d6000602084013e613209565b606091505b509150915061321a878383876132aa565b979650505050505050565b60606000808573ffffffffffffffffffffffffffffffffffffffff168560405161324f9190613e6f565b600060405180830381855af49150503d806000811461328a576040519150601f19603f3d011682016040523d82523d6000602084013e61328f565b606091505b50915091506132a0868383876132aa565b9695505050505050565b606083156133405782516000036133395773ffffffffffffffffffffffffffffffffffffffff85163b613339576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610539565b5081613092565b61309283838151156133555781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105399190613e8b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516101a0810167ffffffffffffffff811182821017156133dc576133dc613389565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561342957613429613389565b604052919050565b600067ffffffffffffffff82111561344b5761344b613389565b5060051b60200190565b73ffffffffffffffffffffffffffffffffffffffff8116811461118b57600080fd5b600082601f83011261348857600080fd5b8135602061349d61349883613431565b6133e2565b82815260059290921b840181019181810190868411156134bc57600080fd5b8286015b848110156134e05780356134d381613455565b83529183019183016134c0565b509695505050505050565b600082601f8301126134fc57600080fd5b8135602061350c61349883613431565b82815260059290921b8401810191818101908684111561352b57600080fd5b8286015b848110156134e0578035835291830191830161352f565b60008060006060848603121561355b57600080fd5b833567ffffffffffffffff8082111561357357600080fd5b61357f87838801613477565b9450602086013591508082111561359557600080fd5b6135a187838801613477565b935060408601359150808211156135b757600080fd5b506135c4868287016134eb565b9150509250925092565b801515811461118b57600080fd5b600080604083850312156135ef57600080fd5b82356135fa81613455565b9150602083013561360a816135ce565b809150509250929050565b6000806040838503121561362857600080fd5b823561363381613455565b946020939093013593505050565b60006020828403121561365357600080fd5b813561365e81613455565b9392505050565b60008083601f84011261367757600080fd5b50813567ffffffffffffffff81111561368f57600080fd5b6020830191508360208260051b85010111156136aa57600080fd5b9250929050565b600080600080604085870312156136c757600080fd5b843567ffffffffffffffff808211156136df57600080fd5b6136eb88838901613665565b9096509450602087013591508082111561370457600080fd5b5061371187828801613665565b95989497509550505050565b60008060008060008060c0878903121561373657600080fd5b863561374181613455565b9860208801359850604088013597606081013597506080810135965060a00135945092505050565b60008060008060008060008060008060008060c08d8f03121561378b57600080fd5b67ffffffffffffffff8d3511156137a157600080fd5b6137ae8e8e358f01613665565b909c509a5067ffffffffffffffff60208e013511156137cc57600080fd5b6137dc8e60208f01358f01613665565b909a50985067ffffffffffffffff60408e013511156137fa57600080fd5b61380a8e60408f01358f01613665565b909850965067ffffffffffffffff60608e0135111561382857600080fd5b6138388e60608f01358f01613665565b909650945067ffffffffffffffff60808e0135111561385657600080fd5b6138668e60808f01358f01613665565b909450925067ffffffffffffffff60a08e0135111561388457600080fd5b6138948e60a08f01358f01613665565b81935080925050509295989b509295989b509295989b565b6000806000606084860312156138c157600080fd5b833567ffffffffffffffff808211156138d957600080fd5b6138e587838801613477565b945060208601359150808211156138fb57600080fd5b50613908868287016134eb565b925050604084013561391981613455565b809150509250925092565b6000806040838503121561393757600080fd5b823561394281613455565b915060208381013567ffffffffffffffff8082111561396057600080fd5b818601915086601f83011261397457600080fd5b81358181111561398657613986613389565b6139b6847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016133e2565b915080825287848285010111156139cc57600080fd5b80848401858401376000848284010152508093505050509250929050565b6000806000606084860312156139ff57600080fd5b833567ffffffffffffffff80821115613a1757600080fd5b613a2387838801613477565b94506020860135915080821115613a3957600080fd5b5061390886828701613477565b60008060408385031215613a5957600080fd5b823567ffffffffffffffff80821115613a7157600080fd5b613a7d86838701613477565b93506020850135915080821115613a9357600080fd5b50613aa085828601613477565b9150509250929050565b6020808252825182820181905260009190848201906040850190845b81811015613af857835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613ac6565b50909695505050505050565b600081518084526020808501945080840160005b83811015613b55578151805173ffffffffffffffffffffffffffffffffffffffff1688528301518388015260409096019590820190600101613b18565b509495945050505050565b6020815260006128aa6020830184613b04565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015613c0d578883037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00185528151805173ffffffffffffffffffffffffffffffffffffffff168452870151878401879052613bfa87850182613b04565b9588019593505090860190600101613b9a565b509098975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215613c5c57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613cc357613cc3613c63565b5060010190565b818103818111156128ad576128ad613c63565b60008060408385031215613cf057600080fd5b505080516020909101519092909150565b8051613d0c81613455565b919050565b805160ff81168114613d0c57600080fd5b60006101a08284031215613d3557600080fd5b613d3d6133b8565b613d4683613d01565b8152613d5460208401613d01565b6020820152613d6560408401613d01565b6040820152613d7660608401613d01565b6060820152613d8760808401613d01565b6080820152613d9860a08401613d01565b60a0820152613da960c08401613d11565b60c0820152613dba60e08401613d11565b60e08201526101008381015190820152610120808401519082015261014080840151908201526101608084015190820152610180928301519281019290925250919050565b600060208284031215613e1157600080fd5b815161365e816135ce565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60005b83811015613e66578181015183820152602001613e4e565b50506000910152565b60008251613e81818460208701613e4b565b9190910192915050565b6020815260008251806020840152613eaa816040850160208701613e4b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122003e3913e1cd94946295374ccdefc488e9eaaaf5bab3d8103d1ca83634818e17364736f6c63430008150033

Deployed Bytecode

0x60806040526004361061018f5760003560e01c80634f1ef286116100d65780638da5cb5b1161007f578063ec1a9dd411610059578063ec1a9dd41461043e578063ef33e9db1461046b578063f2fde38b1461048d57600080fd5b80638da5cb5b146103bc578063c11b353c146103f1578063e9c771b21461041e57600080fd5b8063702b5175116100b0578063702b51751461035a578063715018a61461037a578063801ca1441461038f57600080fd5b80634f1ef286146103045780635274ac3f1461031757806352d1902d1461033757600080fd5b80633659cfe611610138578063432abd5611610112578063432abd56146102a1578063467c9eff146102b45780634c13c1be146102e457600080fd5b80633659cfe61461025b578063386b8e6f1461027b578063392094fc1461028e57600080fd5b806313b14ebb1161016957806313b14ebb146101f05780632520e7ff146102035780632bb393991461024857600080fd5b806303e804611461019b5780630de30836146101bd578063111a399b146101dd57600080fd5b3661019657005b600080fd5b3480156101a757600080fd5b506101bb6101b6366004613546565b6104ad565b005b3480156101c957600080fd5b506101bb6101d83660046135dc565b6107e9565b6101bb6101eb366004613615565b610958565b6101bb6101fe366004613615565b610d2e565b34801561020f57600080fd5b5061023361021e366004613641565b60656020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6101bb6102563660046136b1565b610edf565b34801561026757600080fd5b506101bb610276366004613641565b610f89565b6101bb61028936600461371d565b61118e565b6101bb61029c3660046136b1565b6113f7565b6101bb6102af366004613769565b61149a565b3480156102c057600080fd5b506102336102cf366004613641565b60666020526000908152604090205460ff1681565b3480156102f057600080fd5b506101bb6102ff3660046138ac565b6115de565b6101bb610312366004613924565b6117a6565b34801561032357600080fd5b506101bb6103323660046139ea565b61199c565b34801561034357600080fd5b5061034c611d7d565b60405190815260200161023f565b34801561036657600080fd5b506101bb610375366004613a46565b611e69565b34801561038657600080fd5b506101bb6120e5565b34801561039b57600080fd5b506103af6103aa366004613641565b612124565b60405161023f9190613aaa565b3480156103c857600080fd5b5060335460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161023f565b3480156103fd57600080fd5b5061034c61040c366004613641565b60686020526000908152604090205481565b34801561042a57600080fd5b506101bb6104393660046135dc565b612206565b34801561044a57600080fd5b5061045e610459366004613641565b6122e4565b60405161023f9190613b60565b34801561047757600080fd5b50610480612511565b60405161023f9190613b73565b34801561049957600080fd5b506101bb6104a8366004613641565b61261b565b3360009081526065602052604090205460ff161580156105015750336104e860335473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610542576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f9160048201526024015b60405180910390fd5b8151835114158061055557508051825114155b15610591576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b83518110156107e35760008482815181106105b1576105b1613c1b565b6020026020010151905060008483815181106105cf576105cf613c1b565b6020026020010151905060008484815181106105ed576105ed613c1b565b60200260200101519050600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610671575073ffffffffffffffffffffffffffffffffffffffff83166000908152606860205260409020805490829055610720565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff858116602483015284169063dd62ed3e90604401602060405180830381865afa1580156106e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107079190613c4a565b9050610715838560006126cf565b6107208385846126cf565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260676020526040902061074f9084612888565b5061075b606985612888565b508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167ff07829fca7213317a8d0869b30e579887a8d2a7bfd3a28f25075bd01b370d75684846040516107c4929190918252602082015260400190565b60405180910390a35050505080806107db90613c92565b915050610594565b50505050565b3360009081526065602052604090205460ff1615801561083d57503361082460335473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610879576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b8173ffffffffffffffffffffffffffffffffffffffff81166108cc576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f926004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff831660008181526066602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527fad476fc62f6b1b5d25b35bd756cfbfcd299b581b8dfb25d5492c4305a0969bd291015b60405180910390a2505050565b3360009081526066602052604090205460ff166109a6576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b8015610a4b5773ffffffffffffffffffffffffffffffffffffffff8216600090815260686020526040902054811115610a10576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f966004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff821660009081526068602052604081208054839290610a45908490613cca565b90915550505b6000808373ffffffffffffffffffffffffffffffffffffffff16637d7c2a1c846040518263ffffffff1660e01b8152600401604080518083038185885af1158015610a9a573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610abf9190613cdd565b90925090508215610cd75760008473ffffffffffffffffffffffffffffffffffffffff1663b7791bf26040518163ffffffff1660e01b81526004016101a060405180830381865afa158015610b18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3c9190613d22565b608081015190915073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148015610b7c5750600083125b15610bb8576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f956004820152602401610539565b60a081015173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148015610bf55750600082135b15610c31576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f956004820152602401610539565b608081015173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480610c9a575060a081015173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b610cd5576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f956004820152602401610539565b505b604080518381526020810183905273ffffffffffffffffffffffffffffffffffffffff8616917f1b5a6c038413ee85baaa108b9e0f353fc1fb6dba5c500211baef62b0761e4d88910160405180910390a250505050565b3360009081526066602052604090205460ff16610d7c576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b8015610e215773ffffffffffffffffffffffffffffffffffffffff8216600090815260686020526040902054811115610de6576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f966004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff821660009081526068602052604081208054839290610e1b908490613cca565b90915550505b60008273ffffffffffffffffffffffffffffffffffffffff16637d7c2a1c836040518263ffffffff1660e01b815260040160206040518083038185885af1158015610e70573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610e959190613c4a565b90508273ffffffffffffffffffffffffffffffffffffffff167f2ce777e59f0be485fcc70a4790aa17e9aca8e9e6704e6ab690fc2018c92cca3f8260405161094b91815260200190565b828114610f1d576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b83811015610f8257610f70858583818110610f3d57610f3d613c1b565b9050602002016020810190610f529190613641565b848484818110610f6457610f64613c1b565b90506020020135610958565b80610f7a81613c92565b915050610f20565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000041ffb31bdba1278c073c1018bb988c5368b9b10c16300361104e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610539565b7f00000000000000000000000041ffb31bdba1278c073c1018bb988c5368b9b10c73ffffffffffffffffffffffffffffffffffffffff166110c37f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611166576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610539565b61116f816128b3565b6040805160008082526020820190925261118b918391906128bb565b50565b3360009081526066602052604090205460ff166111dc576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b478515801590611210575073ffffffffffffffffffffffffffffffffffffffff871660009081526068602052604090205486115b1561124c576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f966004820152602401610539565b6040517f1593a34b00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044810184905260648101839052600090819073ffffffffffffffffffffffffffffffffffffffff8a1690631593a34b908a90608401604080518083038185885af11580156112d3573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906112f89190613cdd565b91509150600088118015611316575060008213806113165750600081125b1561139b57600047841161132b576000611335565b6113354785613cca565b905034156113585734811161134b576000611355565b6113553482613cca565b90505b80156113995773ffffffffffffffffffffffffffffffffffffffff8a1660009081526068602052604081208054839290611393908490613cca565b90915550505b505b604080518381526020810183905273ffffffffffffffffffffffffffffffffffffffff8b16917f1b5a6c038413ee85baaa108b9e0f353fc1fb6dba5c500211baef62b0761e4d88910160405180910390a2505050505050505050565b828114611435576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b83811015610f825761148885858381811061145557611455613c1b565b905060200201602081019061146a9190613641565b84848481811061147c5761147c613c1b565b90506020020135610d2e565b8061149281613c92565b915050611438565b8a891415806114a957508a8714155b806114b457508a8514155b806114bf57508a8314155b806114ca57508a8114155b15611506576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b8b8110156115cf576115bd8d8d8381811061152657611526613c1b565b905060200201602081019061153b9190613641565b8c8c8481811061154d5761154d613c1b565b905060200201358b8b8581811061156657611566613c1b565b905060200201358a8a8681811061157f5761157f613c1b565b9050602002013589898781811061159857611598613c1b565b905060200201358888888181106115b1576115b1613c1b565b9050602002013561118e565b806115c781613c92565b915050611509565b50505050505050505050505050565b6115e6612aba565b8151835114611626576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b83518110156107e35773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff1684828151811061166f5761166f613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16036116ba576116b5828483815181106116a8576116a8613c1b565b6020026020010151612b3d565b6116f7565b6116f78482815181106116cf576116cf613c1b565b6020026020010151838584815181106116ea576116ea613c1b565b6020026020010151612b89565b82818151811061170957611709613c1b565b602002602001015184828151811061172357611723613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fed037dbd552690e525e4da3c8b4ea3e5f707bfccda2ea6bb6647480383b52cdd8460405161178c919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b60405180910390a38061179e81613c92565b915050611629565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000041ffb31bdba1278c073c1018bb988c5368b9b10c16300361186b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610539565b7f00000000000000000000000041ffb31bdba1278c073c1018bb988c5368b9b10c73ffffffffffffffffffffffffffffffffffffffff166118e07f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611983576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610539565b61198c826128b3565b611998828260016128bb565b5050565b600054610100900460ff16158080156119bc5750600054600160ff909116105b806119d65750303b1580156119d6575060005460ff166001145b611a62576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610539565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611ac057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b8173ffffffffffffffffffffffffffffffffffffffff8116611b13576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f926004820152602401610539565b60005b8551811015611c0e57600160656000888481518110611b3757611b37613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550858181518110611ba257611ba2613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fb873643b3104ddd26927dec7f9a08aa22d03ac20ada0295a2de7e1a6c60f2a516001604051611bf4911515815260200190565b60405180910390a280611c0681613c92565b915050611b16565b5060005b8451811015611d0a57600160666000878481518110611c3357611c33613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550848181518110611c9e57611c9e613c1b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fad476fc62f6b1b5d25b35bd756cfbfcd299b581b8dfb25d5492c4305a0969bd26001604051611cf0911515815260200190565b60405180910390a280611d0281613c92565b915050611c12565b50611d1483612c2d565b5080156107e357600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050565b60003073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000041ffb31bdba1278c073c1018bb988c5368b9b10c1614611e44576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c00000000000000006064820152608401610539565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b3360009081526065602052604090205460ff16158015611ebd575033611ea460335473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614155b15611ef9576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f916004820152602401610539565b8051825114611f39576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f936004820152602401610539565b60005b82518110156120e0576000838281518110611f5957611f59613c1b565b602002602001015190506000838381518110611f7757611f77613c1b565b6020026020010151905073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611ff35773ffffffffffffffffffffffffffffffffffffffff8216600090815260686020526040812055611fff565b611fff818360006126cf565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260676020526040902061202e9082612ca4565b5073ffffffffffffffffffffffffffffffffffffffff8216600090815260676020526040902061205d90612cc6565b6000036120715761206f606983612ca4565b505b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fd54cf739d89b84837b7ca6e1402410c3497b29b0a5fbe452e57008b3abd8874d60405160405180910390a3505080806120d890613c92565b915050611f3c565b505050565b6120ed612aba565b6040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f946004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260676020526040902060609061215581612cc6565b67ffffffffffffffff81111561216d5761216d613389565b604051908082528060200260200182016040528015612196578160200160208202803683370190505b50915060005b6121a582612cc6565b8110156121ff576121b68282612cd0565b8382815181106121c8576121c8613c1b565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152806121f781613c92565b91505061219c565b5050919050565b61220e612aba565b8173ffffffffffffffffffffffffffffffffffffffff8116612261576040517fc25348e000000000000000000000000000000000000000000000000000000000815262015f926004820152602401610539565b73ffffffffffffffffffffffffffffffffffffffff831660008181526065602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527fb873643b3104ddd26927dec7f9a08aa22d03ac20ada0295a2de7e1a6c60f2a51910161094b565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260676020526040902060609061231581612cc6565b67ffffffffffffffff81111561232d5761232d613389565b60405190808252806020026020018201604052801561237257816020015b604080518082019091526000808252602082015281526020019060019003908161234b5790505b50915060005b61238182612cc6565b8110156121ff5760006123948383612cd0565b9050808483815181106123a9576123a9613c1b565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff918216905281167fffffffffffffffffffffffff1111111111111111111111111111111111111112016124455773ffffffffffffffffffffffffffffffffffffffff8516600090815260686020526040902054845185908490811061243057612430613c1b565b602002602001015160200181815250506124fe565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff868116602483015282169063dd62ed3e90604401602060405180830381865afa1580156124b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124db9190613c4a565b8483815181106124ed576124ed613c1b565b602002602001015160200181815250505b508061250981613c92565b915050612378565b606061251d6069612cc6565b67ffffffffffffffff81111561253557612535613389565b60405190808252806020026020018201604052801561257b57816020015b6040805180820190915260008152606060208201528152602001906001900390816125535790505b50905060005b61258b6069612cc6565b81101561261757600061259f606983612cd0565b9050808383815181106125b4576125b4613c1b565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff90911690526125e2816122e4565b8383815181106125f4576125f4613c1b565b60200260200101516020018190525050808061260f90613c92565b915050612581565b5090565b612623612aba565b73ffffffffffffffffffffffffffffffffffffffff81166126c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610539565b61118b81612c2d565b80158061276f57506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612749573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061276d9190613c4a565b155b6127fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610539565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b3000000000000000000000000000000000000000000000000000000001790526120e0908490612cdc565b60006128aa8373ffffffffffffffffffffffffffffffffffffffff8416612de8565b90505b92915050565b61118b612aba565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff16156128ee576120e083612e37565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612973575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261297091810190613c4a565b60015b6129ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f7420555550530000000000000000000000000000000000006064820152608401610539565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8114612aae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c655555494400000000000000000000000000000000000000000000006064820152608401610539565b506120e0838383612f41565b60335473ffffffffffffffffffffffffffffffffffffffff163314612b3b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610539565b565b60008060008060008587614e20f19050806120e0576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a6004820152602401610539565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806107e3576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a6004820152602401610539565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006128aa8373ffffffffffffffffffffffffffffffffffffffff8416612f66565b60006128ad825490565b60006128aa8383613059565b6000612d3e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166130839092919063ffffffff16565b8051909150156120e05780806020019051810190612d5c9190613dff565b6120e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610539565b6000818152600183016020526040812054612e2f575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556128ad565b5060006128ad565b73ffffffffffffffffffffffffffffffffffffffff81163b612edb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e7472616374000000000000000000000000000000000000006064820152608401610539565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612f4a8361309a565b600082511180612f575750805b156120e0576107e383836130e7565b6000818152600183016020526040812054801561304f576000612f8a600183613cca565b8554909150600090612f9e90600190613cca565b9050818114613003576000866000018281548110612fbe57612fbe613c1b565b9060005260206000200154905080876000018481548110612fe157612fe1613c1b565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061301457613014613e1c565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506128ad565b60009150506128ad565b600082600001828154811061307057613070613c1b565b9060005260206000200154905092915050565b6060613092848460008561310c565b949350505050565b6130a381612e37565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606128aa8383604051806060016040528060278152602001613edd60279139613225565b60608247101561319e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610539565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516131c79190613e6f565b60006040518083038185875af1925050503d8060008114613204576040519150601f19603f3d011682016040523d82523d6000602084013e613209565b606091505b509150915061321a878383876132aa565b979650505050505050565b60606000808573ffffffffffffffffffffffffffffffffffffffff168560405161324f9190613e6f565b600060405180830381855af49150503d806000811461328a576040519150601f19603f3d011682016040523d82523d6000602084013e61328f565b606091505b50915091506132a0868383876132aa565b9695505050505050565b606083156133405782516000036133395773ffffffffffffffffffffffffffffffffffffffff85163b613339576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610539565b5081613092565b61309283838151156133555781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105399190613e8b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516101a0810167ffffffffffffffff811182821017156133dc576133dc613389565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561342957613429613389565b604052919050565b600067ffffffffffffffff82111561344b5761344b613389565b5060051b60200190565b73ffffffffffffffffffffffffffffffffffffffff8116811461118b57600080fd5b600082601f83011261348857600080fd5b8135602061349d61349883613431565b6133e2565b82815260059290921b840181019181810190868411156134bc57600080fd5b8286015b848110156134e05780356134d381613455565b83529183019183016134c0565b509695505050505050565b600082601f8301126134fc57600080fd5b8135602061350c61349883613431565b82815260059290921b8401810191818101908684111561352b57600080fd5b8286015b848110156134e0578035835291830191830161352f565b60008060006060848603121561355b57600080fd5b833567ffffffffffffffff8082111561357357600080fd5b61357f87838801613477565b9450602086013591508082111561359557600080fd5b6135a187838801613477565b935060408601359150808211156135b757600080fd5b506135c4868287016134eb565b9150509250925092565b801515811461118b57600080fd5b600080604083850312156135ef57600080fd5b82356135fa81613455565b9150602083013561360a816135ce565b809150509250929050565b6000806040838503121561362857600080fd5b823561363381613455565b946020939093013593505050565b60006020828403121561365357600080fd5b813561365e81613455565b9392505050565b60008083601f84011261367757600080fd5b50813567ffffffffffffffff81111561368f57600080fd5b6020830191508360208260051b85010111156136aa57600080fd5b9250929050565b600080600080604085870312156136c757600080fd5b843567ffffffffffffffff808211156136df57600080fd5b6136eb88838901613665565b9096509450602087013591508082111561370457600080fd5b5061371187828801613665565b95989497509550505050565b60008060008060008060c0878903121561373657600080fd5b863561374181613455565b9860208801359850604088013597606081013597506080810135965060a00135945092505050565b60008060008060008060008060008060008060c08d8f03121561378b57600080fd5b67ffffffffffffffff8d3511156137a157600080fd5b6137ae8e8e358f01613665565b909c509a5067ffffffffffffffff60208e013511156137cc57600080fd5b6137dc8e60208f01358f01613665565b909a50985067ffffffffffffffff60408e013511156137fa57600080fd5b61380a8e60408f01358f01613665565b909850965067ffffffffffffffff60608e0135111561382857600080fd5b6138388e60608f01358f01613665565b909650945067ffffffffffffffff60808e0135111561385657600080fd5b6138668e60808f01358f01613665565b909450925067ffffffffffffffff60a08e0135111561388457600080fd5b6138948e60a08f01358f01613665565b81935080925050509295989b509295989b509295989b565b6000806000606084860312156138c157600080fd5b833567ffffffffffffffff808211156138d957600080fd5b6138e587838801613477565b945060208601359150808211156138fb57600080fd5b50613908868287016134eb565b925050604084013561391981613455565b809150509250925092565b6000806040838503121561393757600080fd5b823561394281613455565b915060208381013567ffffffffffffffff8082111561396057600080fd5b818601915086601f83011261397457600080fd5b81358181111561398657613986613389565b6139b6847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016133e2565b915080825287848285010111156139cc57600080fd5b80848401858401376000848284010152508093505050509250929050565b6000806000606084860312156139ff57600080fd5b833567ffffffffffffffff80821115613a1757600080fd5b613a2387838801613477565b94506020860135915080821115613a3957600080fd5b5061390886828701613477565b60008060408385031215613a5957600080fd5b823567ffffffffffffffff80821115613a7157600080fd5b613a7d86838701613477565b93506020850135915080821115613a9357600080fd5b50613aa085828601613477565b9150509250929050565b6020808252825182820181905260009190848201906040850190845b81811015613af857835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613ac6565b50909695505050505050565b600081518084526020808501945080840160005b83811015613b55578151805173ffffffffffffffffffffffffffffffffffffffff1688528301518388015260409096019590820190600101613b18565b509495945050505050565b6020815260006128aa6020830184613b04565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015613c0d578883037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00185528151805173ffffffffffffffffffffffffffffffffffffffff168452870151878401879052613bfa87850182613b04565b9588019593505090860190600101613b9a565b509098975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215613c5c57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613cc357613cc3613c63565b5060010190565b818103818111156128ad576128ad613c63565b60008060408385031215613cf057600080fd5b505080516020909101519092909150565b8051613d0c81613455565b919050565b805160ff81168114613d0c57600080fd5b60006101a08284031215613d3557600080fd5b613d3d6133b8565b613d4683613d01565b8152613d5460208401613d01565b6020820152613d6560408401613d01565b6040820152613d7660608401613d01565b6060820152613d8760808401613d01565b6080820152613d9860a08401613d01565b60a0820152613da960c08401613d11565b60c0820152613dba60e08401613d11565b60e08201526101008381015190820152610120808401519082015261014080840151908201526101608084015190820152610180928301519281019290925250919050565b600060208284031215613e1157600080fd5b815161365e816135ce565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60005b83811015613e66578181015183820152602001613e4e565b50506000910152565b60008251613e81818460208701613e4b565b9190910192915050565b6020815260008251806020840152613eaa816040850160208701613e4b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122003e3913e1cd94946295374ccdefc488e9eaaaf5bab3d8103d1ca83634818e17364736f6c63430008150033

Block Transaction 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
[ 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.