Vault

Description:

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

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/main/Vault.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.25;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "./common/Constants.sol";
import "./vault/VaultYieldBasic.sol";
import "../interfaces/ISuperStateOracle.sol";
import "../interfaces/ISwapper.sol";

/**
 * @title Vault contract
 * @author Naturelab
 * @dev This contract is the logical implementation of the vault,
 * and its main purpose is to provide users with a gateway for depositing
 * and withdrawing funds and to manage user shares.
 */
contract Vault is VaultYieldBasic, Constants {
    using SafeERC20 for IERC20;
    using EnumerableSet for EnumerableSet.AddressSet;

    // The oracle contract of superstate
    ISuperStateOracle internal constant USCC_ORACLE = ISuperStateOracle(0xAfFd8F5578E8590665de561bdE9E7BAdb99300d9);

    uint256 public constant VERSION = 1;
    bool public depositHalted;

    modifier whenDepositNotHalted() {
        if (depositHalted) {
            revert Errors.DepositHalted();
        }
        _;
    }

    event DepositHalted();
    event DepositResumed();

    constructor(uint8 _decimals) VaultYieldBasic(_decimals) {}

    function haltDeposit() external onlyOwner {
        depositHalted = true;
        emit DepositHalted();
    }

    function resumeDeposit() external onlyOwner {
        depositHalted = false;
        emit DepositResumed();
    }

    /**
     * @dev override to include USCC value
     */
    function underlyingTvl() public override returns (uint256) {
        (, uint256 usccPrice_,,uint256 latstUpdateAt_,) = USCC_ORACLE.latestRoundData(); // answer with 6 decimals
        if (latstUpdateAt_ + MAX_ORACLE_PRICE_UPDATED_PERIOD < block.timestamp) {
            revert Errors.OraclePriceTooOld();
        }
        uint256 totalAssets_ = IERC20(asset()).balanceOf(address(this));
        uint256 usccAmountInUsdc_ = IERC20(USCC).balanceOf(address(this)) * usccPrice_ / 1e6;
        uint256 totalBal_ = totalAssets_ + usccAmountInUsdc_;

        uint256 totalStrategy_ = totalStrategiesAssets();
        return totalStrategy_ + totalBal_ - vaultState.revenue;
    }

    /**
     * @dev Redemption operation executed by the redeemOperator. Currently, only fBTC redemptions are supported.
     * @param _shares The amount of share tokens to be redeemed.
     * @param _receiver The address of the receiver of the assets.
     * @param _owner The owner address of the shares.
     * @return assets_ The amount of assets obtained.
     */

    function optionalRedeem(address, uint256 _shares, uint256, address _receiver, address _owner)
        public
        override
        nonReentrant
        whenNotPaused
        returns (uint256 assets_)
    {
        if (msg.sender != vaultParams.redeemOperator) revert Errors.UnSupportedOperation();

        if (_shares == type(uint256).max) {
            _shares = maxRedeem(_owner);
        } else {
            require(_shares <= maxRedeem(_owner), "ERC4626: redeem more than max");
        }

        if (msg.sender != _owner) {
            _spendAllowance(_owner, msg.sender, _shares);
        }

        assets_ = previewRedeem(_shares);
        _burn(_owner, _shares);
        IERC20(asset()).safeTransfer(_receiver, assets_);

        emit OptionalRedeem(asset(), _shares, _receiver, _owner);
    }

    /**
     * @dev Internal function to calculate the shares issued for a deposit.
     * @param _assets The amount of assets to deposit.
     * @param _receiver The address of the receiver of the shares.
     * @return shares_ The amount of shares issued.
     */
    function optionalDepositDeal(uint256 _assets, address _receiver) internal returns (uint256 shares_) {
        uint256 maxAssets = maxDeposit(_receiver);
        if (_assets > maxAssets) {
            revert ERC4626ExceededMaxDeposit(_receiver, _assets, maxAssets);
        }
        shares_ = previewDeposit(_assets);

        emit Deposit(msg.sender, _receiver, _assets, shares_);
    }

    function optionalDeposit(
        address _token,
        uint256 _assets,
        address _receiver,
        address _referral,
        bytes memory _swapData,
        uint256 _swapGetMin
    )
        public
        virtual
        override
        nonReentrant
        whenNotPaused
        whenPriceUpdated
        whenDepositNotHalted
        returns (uint256 shares_)
    {
        if (!tokens.contains(_token)) revert Errors.UnsupportedToken();
        if (_assets == type(uint256).max) {
            _assets = IERC20(_token).balanceOf(msg.sender);
        }

        IERC20(_token).safeTransferFrom(msg.sender, address(this), _assets);

        if (_token != USDC) {
            if (_token == USCC) {
                uint8 usccPriceDecimals_ = USCC_ORACLE.decimals();
                (, uint256 usccPrice_,,uint256 latestUpdateAt_,) = USCC_ORACLE.latestRoundData();
                if (latestUpdateAt_ + MAX_ORACLE_PRICE_UPDATED_PERIOD < block.timestamp) {
                    revert Errors.OraclePriceTooOld();
                }
                _assets = _assets * usccPrice_ / (10 ** usccPriceDecimals_);
            } else {
                IERC20(_token).safeTransfer(strategyAddress(0), _assets);
                _assets = ISwapper(strategyAddress(0)).convertTokensToUSDC(_token, _assets, _swapData, _swapGetMin);
            }
        }

        if (_assets < minDepositAmount) revert Errors.DepositAmountTooSmall();

        shares_ = optionalDepositDeal(_assets, _receiver);
        _mint(_receiver, shares_);
        emit OptionalDeposit(msg.sender, _token, _assets, _receiver, _referral);
    }

    function deposit(uint256 _assets, address _receiver)
        public
        virtual
        override
        nonReentrant
        whenNotPaused
        whenPriceUpdated
        whenDepositNotHalted
        returns (uint256 shares_)
    {
        if (_assets == type(uint256).max) {
            _assets = IERC20(asset()).balanceOf(msg.sender);
        }
        if (_assets < minDepositAmount) revert Errors.DepositAmountTooSmall();
        shares_ = ERC4626Upgradeable.deposit(_assets, _receiver);
    }

    function mint(uint256 _shares, address _receiver)
        public
        virtual
        override
        nonReentrant
        whenNotPaused
        whenPriceUpdated
        whenDepositNotHalted
        returns (uint256 assets_)
    {
        assets_ = ERC4626Upgradeable.mint(_shares, _receiver);
        if (assets_ < minDepositAmount) revert Errors.DepositAmountTooSmall();
    }
}
"
    },
    "dependencies/@openzeppelin-contracts-5.0.2/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../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;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` 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.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // 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 cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
"
    },
    "dependencies/@openzeppelin-contracts-5.0.2/utils/structs/EnumerableSet.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ```solidity
 * 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 is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 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 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

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

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

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

            // Delete the tracked position for the deleted slot
            delete set._positions[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._positions[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;
    }
}
"
    },
    "src/main/common/Constants.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.25;

abstract contract Constants {
    address public constant USCC = 0x14d60E7FDC0D71d8611742720E4C50E7a974020c; // decimals: 6
    address public constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; // decimals: 6
    address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // decimals: 6
    address public constant USDS = 0xdC035D45d973E3EC169d2276DDab16f1e407384F; // decimals: 18
    address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; // decimales: 18

    // Time check for price update, 100 hours
    uint256 public constant MAX_ORACLE_PRICE_UPDATED_PERIOD = 100 hours;
}
"
    },
    "src/main/vault/VaultYieldBasic.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.25;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import "../../interfaces/IRedeemOperator.sol";
import "../../interfaces/IStrategy.sol";
import "../../interfaces/IVault.sol";
import "../libraries/StorageSlot.sol";
import "../libraries/Errors.sol";
import "./StrategyFactory.sol";

/**
 * @title VaultYieldBasic contract
 * @author Naturelab
 * @dev This contract is the logical implementation of the vault,
 * and its main purpose is to provide users with a gateway for depositing
 * and withdrawing funds and to manage user shares.
 */
contract VaultYieldBasic is
    IVault,
    StrategyFactory,
    ERC4626Upgradeable,
    PausableUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20 for IERC20;
    using StorageSlot for *;
    using EnumerableSet for EnumerableSet.AddressSet;

    // Use EIP-1153 to temporarily store prices for calculation.
    bytes32 internal constant EXCHANGE_PRICE_CACHE = keccak256("EXCHANGE_PRICE_CACHE");

    // Define a constant variable representing the fee denominator, 10000 (used for percentage calculations)
    uint256 internal constant FEE_DENOMINATOR = 1e4;

    // Up to 0.04% can be charged as a management fee in each cycle (4 / 10000)
    uint256 internal constant MAX_MANAGEMENT_FEE_RATE = 4;

    // The shortest cycle for charging the management fee is 7 days
    uint256 internal constant MIN_MANAGEMENT_FEE_CLAIM_PERIOD = 7 days;

    // The maximum interval for price updates. If prices are not updated for a long time,
    // deposits will be temporarily unavailable.
    uint256 internal constant MAX_PRICE_UPDATED_PERIOD = 14 days;

    // The maximum fee for withdrawing from the idle treasury is 1.2% (120 / 10000)
    uint256 internal constant MAX_EXIT_FEE_RATE = 120;

    // The maximum revenue fee rate is 25% (2500 / 10000)
    uint256 internal constant MAX_REVENUE_FEE_RATE = 2500;

    // Prevents erroneous price fluctuations. (200 / 10000)
    uint256 internal constant MAX_PRICE_CHANGE_RATE = 200;

    // Units of measurement used for precise calculations.
    uint256 internal immutable PRECISION;

    // Used to determine the initial exchange price.
    uint256 internal immutable INIT_EXCHANGE_PRICE;

    // Used to limit the minimum initial price.
    uint256 internal immutable MIN_MARKET_CAPACITY;

    // Vault parameters, encapsulating the configuration of the vault
    VaultParams internal vaultParams;

    // Vault state, encapsulating the state of the vault
    VaultState internal vaultState;

    // Used to manage tokens allowed to be deposited.
    EnumerableSet.AddressSet internal tokens;

    // The minimum amount of assets that can be deposited, in usdc.
    uint256 public minDepositAmount;

    /**
     * @dev  Ensure that this method is only called by authorized portfolio managers.
     */
    modifier onlyRebalancer() {
        if (msg.sender != vaultParams.rebalancer) revert Errors.CallerNotRebalancer();
        _;
    }

    /**
     * @dev Ensure that the price has been updated within the specified period.
     */
    modifier whenPriceUpdated() {
        if (vaultParams.maxPriceUpdatePeriod < block.timestamp - vaultState.lastUpdatePriceTime) {
            revert Errors.PriceNotUpdated();
        }
        _;
    }

    constructor(uint8 _decimals) {
        PRECISION = 10 ** _decimals;
        INIT_EXCHANGE_PRICE = PRECISION;
        MIN_MARKET_CAPACITY = PRECISION * 10000;
    }

    /**
     * @dev Initialize various parameters of the Vault contract.
     * @param _initBytes The encoded initialization parameters.
     */
    function initialize(bytes calldata _initBytes) external initializer {
        (VaultParams memory params_, address[] memory tokens_, uint256 minDepositAmount_) = abi.decode(_initBytes, (IVault.VaultParams, address[], uint256));

        __Pausable_init();
        __ReentrancyGuard_init();
        __ERC20_init(params_.name, params_.symbol);

        if (params_.underlyingToken == address(0)) revert Errors.InvalidUnderlyingToken();
        if (params_.rebalancer == address(0)) revert Errors.InvalidRebalancer();
        if (params_.admin == address(0)) revert Errors.InvalidAdmin();
        if (params_.feeReceiver == address(0)) revert Errors.InvalidFeeReceiver();
        if (params_.marketCapacity < MIN_MARKET_CAPACITY) revert Errors.MarketCapacityTooLow();
        if (params_.managementFeeRate > MAX_MANAGEMENT_FEE_RATE) revert Errors.ManagementFeeRateTooHigh();
        if (params_.managementFeeClaimPeriod < MIN_MANAGEMENT_FEE_CLAIM_PERIOD) {
            revert Errors.ManagementFeeClaimPeriodTooShort();
        }
        if (params_.maxPriceUpdatePeriod > MAX_PRICE_UPDATED_PERIOD) revert Errors.PriceUpdatePeriodTooLong();
        if (params_.revenueRate > MAX_REVENUE_FEE_RATE) revert Errors.RevenueFeeRateTooHigh();
        if (params_.exitFeeRate > MAX_EXIT_FEE_RATE) revert Errors.ExitFeeRateTooHigh();
        __Ownable_init(params_.admin);
        __ERC4626_init(IERC20(params_.underlyingToken));
        vaultState.lastClaimMngFeeTime = block.timestamp;
        vaultState.lastUpdatePriceTime = block.timestamp;
        vaultState.exchangePrice = INIT_EXCHANGE_PRICE;
        vaultParams = params_;
        minDepositAmount = minDepositAmount_;

        for (uint256 i; i < tokens_.length; i++) {
            if (tokens_[i] == address(0)) revert Errors.InvalidUnderlyingToken();
            tokens.add(tokens_[i]);
        }
    }

    /**
     * @dev Returns the vault parameters.
     * @return A struct containing the vault parameters.
     */
    function getVaultParams() public view returns (VaultParams memory) {
        return vaultParams;
    }

    /**
     * @dev Returns the vault state.
     * @return A struct containing the vault state.
     */
    function getVaultState() public view returns (VaultState memory) {
        return vaultState;
    }

    function getPrecison() public view returns (uint256) {
        return PRECISION;
    }

    function getTokens() public view returns (address[] memory) {
        return tokens.values();
    }

    /**
     * @dev Update the size of the pool's capacity.
     * @param _newCapacityLimit The new size of the capacity.
     */
    function updateMarketCapacity(uint256 _newCapacityLimit) external onlyOwner {
        if (_newCapacityLimit <= vaultParams.marketCapacity) revert Errors.UnSupportedOperation();
        emit UpdateMarketCapacity(vaultParams.marketCapacity, _newCapacityLimit);
        vaultParams.marketCapacity = _newCapacityLimit;
    }

    /**
     * @dev Update the management fee rate.
     * @param _newManagementFeeRate The new rate.
     */
    function updateManagementFee(uint256 _newManagementFeeRate) external onlyOwner {
        if (_newManagementFeeRate > MAX_MANAGEMENT_FEE_RATE) revert Errors.ManagementFeeRateTooHigh();
        emit UpdateManagementFee(vaultParams.managementFeeRate, _newManagementFeeRate);
        vaultParams.managementFeeRate = _newManagementFeeRate;
    }

    /**
     * @dev Update the collection cycle of management fees.
     * @param _newmanagementFeeClaimPeriod The new management fee claim period.
     */
    function updateManagementFeeClaimPeriod(uint256 _newmanagementFeeClaimPeriod) external onlyOwner {
        if (_newmanagementFeeClaimPeriod < MIN_MANAGEMENT_FEE_CLAIM_PERIOD) {
            revert Errors.ManagementFeeClaimPeriodTooShort();
        }
        emit UpdateManagementFeeClaimPeriod(vaultParams.managementFeeClaimPeriod, _newmanagementFeeClaimPeriod);
        vaultParams.managementFeeClaimPeriod = _newmanagementFeeClaimPeriod;
    }

    /**
     * @dev Update the maximum allowed price update period.
     * @param _newMaxPriceUpdatePeriod The new period.
     */
    function updateMaxPriceUpdatePeriod(uint256 _newMaxPriceUpdatePeriod) external onlyOwner {
        if (_newMaxPriceUpdatePeriod > MAX_PRICE_UPDATED_PERIOD) revert Errors.PriceUpdatePeriodTooLong();
        emit UpdateMaxPriceUpdatePeriod(vaultParams.maxPriceUpdatePeriod, _newMaxPriceUpdatePeriod);
        vaultParams.maxPriceUpdatePeriod = _newMaxPriceUpdatePeriod;
    }

    /**
     * @dev Update the revenue fee rate.
     * @param _newRevenueRate The new rate.
     */
    function updateRevenueRate(uint256 _newRevenueRate) external onlyOwner {
        if (_newRevenueRate > MAX_REVENUE_FEE_RATE) revert Errors.RevenueFeeRateTooHigh();
        emit UpdateRevenueRate(vaultParams.revenueRate, _newRevenueRate);
        vaultParams.revenueRate = _newRevenueRate;
    }

    /**
     * @dev Update the exit fee rate.
     * @param _newExitFeeRate The new rate.
     */
    function updateExitFeeRate(uint256 _newExitFeeRate) external onlyOwner {
        if (_newExitFeeRate > MAX_EXIT_FEE_RATE) revert Errors.ExitFeeRateTooHigh();
        emit UpdateExitFeeRate(vaultParams.exitFeeRate, _newExitFeeRate);
        vaultParams.exitFeeRate = _newExitFeeRate;
    }

    /**
     * @dev Add a new address to the position adjustment whitelist.
     * @param _newRebalancer The new address to be added.
     */
    function updateRebalancer(address _newRebalancer) external onlyOwner {
        if (_newRebalancer == address(0)) revert Errors.InvalidRebalancer();
        emit UpdateRebalancer(vaultParams.rebalancer, _newRebalancer);
        vaultParams.rebalancer = _newRebalancer;
    }

    /**
     * @dev Update the address of the recipient for management fees.
     * @param _newFeeReceiver The new address of the recipient for management fees.
     */
    function updateFeeReceiver(address _newFeeReceiver) external onlyOwner {
        if (_newFeeReceiver == address(0)) revert Errors.InvalidFeeReceiver();
        emit UpdateFeeReceiver(vaultParams.feeReceiver, _newFeeReceiver);
        vaultParams.feeReceiver = _newFeeReceiver;
    }

    /**
     * @dev Update the temporary address of shares when users redeem.
     * @param _newRedeemOperator The new redeem operator address.
     */
    function updateRedeemOperator(address _newRedeemOperator) external onlyOwner {
        if (_newRedeemOperator == address(0)) revert Errors.InvalidRedeemOperator();
        emit UpdateRedeemOperator(vaultParams.redeemOperator, _newRedeemOperator);
        vaultParams.redeemOperator = _newRedeemOperator;
    }

    /**
     * @dev Update the minimum amount of assets in usdc that can be deposited.
     */
    function updateminDepositAmount(uint256 _newminDepositAmount) external onlyOwner {
        emit UpdateminDepositAmount(minDepositAmount, _newminDepositAmount);
        minDepositAmount = _newminDepositAmount;
    }

    function addToken(address _newToken) external onlyOwner {
        if (_newToken == address(0)) revert Errors.InvalidUnderlyingToken();
        tokens.add(_newToken);

        emit AddToken(_newToken);
    }

    function removeToken(address _token) external onlyOwner {
        tokens.remove(_token);

        emit RemoveToken(_token);
    }

    /*
     * @return newExchangePrice The new exercise price
     * @return newRevenue The new realized profit.
     */
    function updateExchangePrice() external onlyRebalancer returns (uint256 newExchangePrice, uint256 newRevenue) {
        EXCHANGE_PRICE_CACHE.asUint256().tstore(vaultState.exchangePrice);
        vaultState.lastUpdatePriceTime = block.timestamp;
        uint256 totalSupply_ = totalSupply();
        if (totalSupply_ == 0) {
            return (vaultState.exchangePrice, vaultState.revenue);
        }
        uint256 currentNetAssets_ = underlyingTvl();
        newExchangePrice = currentNetAssets_ * PRECISION / totalSupply_;
        if (newExchangePrice > vaultState.revenueExchangePrice) {
            if (vaultState.revenueExchangePrice == 0) {
                vaultState.revenueExchangePrice = newExchangePrice;
                vaultState.exchangePrice = newExchangePrice;
                return (vaultState.exchangePrice, vaultState.revenue);
            }
            uint256 newProfit_ = currentNetAssets_ - ((vaultState.revenueExchangePrice * totalSupply_) / PRECISION);
            newRevenue = (newProfit_ * vaultParams.revenueRate) / FEE_DENOMINATOR;
            vaultState.revenue += newRevenue;
            uint256 oldExchangePrice_ = vaultState.exchangePrice;
            vaultState.exchangePrice = ((currentNetAssets_ - newRevenue) * PRECISION) / totalSupply_;
            if (vaultState.exchangePrice - oldExchangePrice_ > oldExchangePrice_ * MAX_PRICE_CHANGE_RATE / 1e4) {
                revert Errors.IncorrectState();
            }
            vaultState.revenueExchangePrice = vaultState.exchangePrice;
        } else {
            uint256 diffExchangePrice_ = vaultState.exchangePrice > newExchangePrice
                ? vaultState.exchangePrice - newExchangePrice
                : newExchangePrice - vaultState.exchangePrice;
            if (diffExchangePrice_ > vaultState.exchangePrice * MAX_PRICE_CHANGE_RATE / 1e4) {
                revert Errors.IncorrectState();
            }
            vaultState.exchangePrice = newExchangePrice;
        }

        emit UpdateExchangePrice(newExchangePrice, newRevenue);
    }

    /**
     * @dev Transfer tokens to a strategy.
     * @param _token The address of the token to transfer.
     * @param _amount The amount of tokens to transfer.
     * @param _strategyIndex The index of the strategy to transfer to.
     */
    function transferToStrategy(address _token, uint256 _amount, uint256 _strategyIndex) external {
        address caller_ = msg.sender;
        if (_strategyIndex == 0) {
            if (caller_ != owner() && caller_ != vaultParams.rebalancer) revert Errors.InvalidOperator();
        } else {
            if (caller_ != owner()) revert Errors.InvalidOperator();
        }
        address strategyAddress_ = strategyAddress(_strategyIndex);
        uint256 positionLimit_ = positionLimit[strategyAddress_];
        uint256 nowAssets_ = IStrategy(strategyAddress_).getNetAssets();
        uint8 coreDecimals_ = decimals();
        uint8 tokenDecimals_ = IERC20Metadata(_token).decimals();
        uint256 transferAsset_ = _amount;
        if (tokenDecimals_ > coreDecimals_) {
            transferAsset_ = _amount / (10 ** (tokenDecimals_ - coreDecimals_));
        } else if (tokenDecimals_ < coreDecimals_) {
            transferAsset_ = _amount * (10 ** (coreDecimals_ - tokenDecimals_));
        }
        if ((nowAssets_ + transferAsset_) > (totalAssets() * positionLimit_ / 1e4)) revert Errors.InvalidLimit();
        IERC20(_token).safeIncreaseAllowance(strategyAddress_, _amount);
        if (!IStrategy(strategyAddress_).onTransferIn(_token, _amount)) revert Errors.IncorrectState();

        emit TransferToStrategy(_token, _amount, _strategyIndex);
    }

    /**
     * @dev Retrieve the amount of the exit fee.
     * @param _assetAmount The amount of asset to be withdrawn.
     * @return withdrawFee_ The exit fee to be deducted.
     */
    function getWithdrawFee(uint256 _assetAmount) public view returns (uint256 withdrawFee_) {
        withdrawFee_ = _assetAmount * vaultParams.exitFeeRate / FEE_DENOMINATOR;
    }

    /**
     * @dev Retrieve the total value locked (TVL) in underlying assets.
     * @return The total value locked in underlying assets.
     */
    function underlyingTvl() public virtual returns (uint256) {
        uint256 totalBal_ = IERC20(asset()).balanceOf(address(this));

        uint256 totalStrategy_ = totalStrategiesAssets();
        return totalStrategy_ + totalBal_ - vaultState.revenue;
    }

    /**
     * @dev Retrieve the amount of assets in the strategy pool.
     * @return The total assets in the strategy pool.
     */
    function totalAssets() public view virtual override whenPriceUpdated returns (uint256) {
        return vaultState.exchangePrice * totalSupply() / PRECISION;
    }

    /**
     * @return Actual LP price during the user's deposit phase.
     */
    function exchangePrice() public view override returns (uint256) {
        return vaultState.exchangePrice;
    }

    /**
     * @dev When the actual LP price exceeds this price, performance fee settlement can be conducted.
     * @return LP price for settling performance fees.
     */
    function revenueExchangePrice() public view override returns (uint256) {
        return vaultState.revenueExchangePrice;
    }

    /**
     * @return Currently accumulated performance fees.
     */
    function revenue() public view override returns (uint256) {
        return vaultState.revenue;
    }

    /**
     * @return The remaining time. If it is 0, deposits are currently not allowed.
     * @dev If it is not 0, the admin needs to update the price within this period.
     */
    function remainingUpdateTime() public view returns (uint256) {
        uint256 timeDiff_ = block.timestamp - vaultState.lastUpdatePriceTime;
        return vaultParams.maxPriceUpdatePeriod > timeDiff_ ? (vaultParams.maxPriceUpdatePeriod - timeDiff_) : 0;
    }

    /**
     * @dev Retrieve the maximum amount that can be deposited by an address.
     * @return maxAssets_ The maximum deposit amount.
     */
    function maxDeposit(address) public view override returns (uint256 maxAssets_) {
        maxAssets_ = vaultParams.marketCapacity - totalAssets();
    }

    /**
     * @return The actual LP price before the last update.
     * @dev If it is lower than current price, there might be a withdrawal rebalancing loss,
     * which the user needs to bear. This usually does not happen.
     */
    function lastExchangePrice() public view override returns (uint256) {
        return EXCHANGE_PRICE_CACHE.asUint256().tload();
    }

    /**
     * @dev Optional deposit function allowing deposits in different token types.
     * @param _token The address of the token to deposit.
     * @param _assets The amount of assets to deposit.
     * @param _receiver The address of the receiver of the shares.
     * @param _referral  Address of the referrer.
     * @return shares_ The amount of shares issued.
     */
    function optionalDeposit(address _token, uint256 _assets, address _receiver, address _referral, bytes memory, uint256)
        public
        virtual
        nonReentrant
        whenNotPaused
        returns (uint256 shares_)
    {
        shares_ = super.deposit(_assets, _receiver);

        emit OptionalDeposit(msg.sender, _token, _assets, _receiver, _referral);
    }

    /**
     * @dev Redemption operation executed by the redeemOperator.
     * @param _token The address of the token to be redeemed.
     * @param _shares The amount of share tokens to be redeemed.
     * @param _cutPercentage The percentage of the rebalancing loss incurred.
     * @param _receiver The address of the receiver of the assets.
     * @param _owner The owner address of the shares.
     * @return assetsAfterFee_ The amount of assets obtained.
     */
    function optionalRedeem(address _token, uint256 _shares, uint256 _cutPercentage, address _receiver, address _owner)
        public
        virtual
        override
        nonReentrant
        whenNotPaused
        returns (uint256 assetsAfterFee_)
    {
        if (!tokens.contains(_token)) revert Errors.InvalidAsset();
        if (msg.sender != vaultParams.redeemOperator) revert Errors.UnSupportedOperation();
        if (vaultState.lastUpdatePriceTime != block.timestamp) revert Errors.PriceNotUpdated();

        if (_shares == type(uint256).max) {
            _shares = maxRedeem(_owner);
        } else {
            require(_shares <= maxRedeem(_owner), "ERC4626: redeem more than max");
        }

        if (msg.sender != _owner) {
            _spendAllowance(_owner, msg.sender, _shares);
        }

        uint256 assets_ = previewRedeem(_shares * (PRECISION - _cutPercentage) / PRECISION);
        _burn(_owner, _shares);
        assetsAfterFee_ = assets_ - getWithdrawFee(assets_);
        IERC20(asset()).safeTransfer(_receiver, assetsAfterFee_);

        emit OptionalRedeem(asset(), _shares, _receiver, _owner);
    }

    /**
     * @dev The deposit method of ERC4626, with the parameter being the amount of assets.
     * @param _assets The amount of asset being deposited.
     * @param _receiver The recipient of the share tokens.
     * @return shares_ The amount of share tokens obtained.
     */
    function deposit(uint256 _assets, address _receiver)
        public
        virtual
        override
        nonReentrant
        whenNotPaused
        returns (uint256 shares_)
    {
        if (_assets == type(uint256).max) {
            _assets = IERC20(asset()).balanceOf(msg.sender);
        }
        shares_ = super.deposit(_assets, _receiver);
    }

    /**
     * @dev The deposit method of ERC4626, with the parameter being the amount of share tokens.
     * @param _shares The amount of share tokens to be minted.
     * @param _receiver The recipient of the share tokens.
     * @return assets_ The amount of assets consumed.
     */
    function mint(uint256 _shares, address _receiver)
        public
        virtual
        override
        nonReentrant
        whenNotPaused
        returns (uint256 assets_)
    {
        assets_ = super.mint(_shares, _receiver);
    }

    function withdraw(uint256, address, address) public virtual override returns (uint256) {
        // Only delayed withdrawals are supported
        revert Errors.NotSupportedYet();
    }

    function redeem(uint256, address, address) public virtual override returns (uint256) {
        // Only delayed withdrawals are supported
        revert Errors.NotSupportedYet();
    }

    /**
     * @dev When a user applies for redemption, his share will be
     * transferred to the RedeemOperator address.
     * @param _shares The amount of share tokens to be redeemed.
     * @param _token The address of the token to redeem.
     */
    function requestRedeem(uint256 _shares, address _token) external virtual nonReentrant whenNotPaused {
        if (_shares == 0) revert Errors.WithdrawZero();
        _transfer(msg.sender, vaultParams.redeemOperator, _shares);
        IRedeemOperator(vaultParams.redeemOperator).registerWithdrawal(msg.sender, _shares);

        emit RequestRedeem(msg.sender, _shares, _token);
    }

    /**
     * @dev Collect management fee.
     */
    function collectManagementFee() external {
        if (msg.sender != vaultParams.feeReceiver) revert Errors.InvalidFeeReceiver();
        uint256 nowTime_ = block.timestamp;
        if (nowTime_ - vaultState.lastClaimMngFeeTime < vaultParams.managementFeeClaimPeriod) {
            revert Errors.InvalidClaimTime();
        }
        vaultState.lastClaimMngFeeTime = nowTime_;
        uint256 assets_ = totalAssets() * vaultParams.managementFeeRate / FEE_DENOMINATOR;
        IERC20(asset()).safeTransfer(vaultParams.feeReceiver, assets_);

        emit CollectManagementFee(assets_);
    }

    /**
     * @dev Collect performance fees to the recipient address.
     */
    function collectRevenue() external {
        if (msg.sender != vaultParams.feeReceiver) revert Errors.InvalidFeeReceiver();
        IERC20(asset()).safeTransfer(vaultParams.feeReceiver, vaultState.revenue);
        emit CollectRevenue(vaultState.revenue);
        vaultState.revenue = 0;
    }

    function pause() external {
        if (msg.sender != owner() && msg.sender != vaultParams.rebalancer) revert Errors.UnSupportedOperation();
        _pause();
    }

    function unpause() external onlyOwner {
        _unpause();
    }
}
"
    },
    "src/interfaces/ISuperStateOracle.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.25;

interface ISuperStateOracle {
    function decimals() external view returns (uint8);
    function latestRoundData() external view returns (uint80 roundId, uint256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}"
    },
    "src/interfaces/ISwapper.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.25;

interface ISwapper {
    function convertTokensToUSDC(address _token, uint256 _amount, bytes memory _swapData, uint256 _swapGetMin)
        external
        returns (uint256 usdcAmount);
}
"
    },
    "dependencies/@openzeppelin-contracts-5.0.2/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}
"
    },
    "dependencies/@openzeppelin-contracts-5.0.2/token/ERC20/extensions/IERC20Permit.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
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].
     *
     * CAUTION: See Security Considerations above.
     */
    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);
}
"
    },
    "dependencies/@openzeppelin-contracts-5.0.2/utils/Address.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) 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)
   

Tags:
ERC20, Multisig, Mintable, Pausable, Swap, Yield, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x4b00a9fddfdef18f5bb99f9413ef2423c4882da8|verified:true|block:23588372|tx:0x013a68950f73b5478418a7c1cc1a6af519442ae2f590717c91425872b57b83d3|first_check:1760606167

Submitted on: 2025-10-16 11:16:10

Comments

Log in to comment.

No comments yet.