Contract 0x8d697440b392269fefD5588e0A023dA283640312

 
 
Txn Hash
Method
Block
From
To
Value [Txn Fee]
0x9b3751340beac370a4d3e29a471d28c41bc7736e9b02dfb8d588e5aedd1fe320Initialize321576672022-08-21 15:57:35286 days 2 hrs ago0x94888e209a5b258ddd2e9621c614499fd3f0c195 IN  0x8d697440b392269fefd5588e0a023da2836403120 MATIC0.002843100003 100.000000108
0x6bf008e601fd872cdd5efd94dd5412d4fd0defc4a955fd93d50378e79062ce8b0x60806040290791772022-06-02 15:41:29366 days 2 hrs agoFurucombo: Deployer IN  Create: FundImplementation0 MATIC0.166436107584 32.348142015
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FundImplementation

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 28 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

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

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

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

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

File 3 of 28 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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);
}

File 4 of 28 : 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 5 of 28 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.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));
        }
    }

    /**
     * @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 6 of 28 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

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

pragma solidity ^0.8.0;

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

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

File 8 of 28 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 9 of 28 : ABDKMath64x64.sol
// SPDX-License-Identifier: BSD-4-Clause
/*
 * ABDK Math 64.64 Smart Contract Library.  Copyright © 2019 by ABDK Consulting.
 * Author: Mikhail Vladimirov <[email protected]>
 */
pragma solidity ^0.8.0;

/**
 * Smart contract library of mathematical functions operating with signed
 * 64.64-bit fixed point numbers.  Signed 64.64-bit fixed point number is
 * basically a simple fraction whose numerator is signed 128-bit integer and
 * denominator is 2^64.  As long as denominator is always the same, there is no
 * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
 * represented by int128 type holding only the numerator.
 */
library ABDKMath64x64 {
  /*
   * Minimum value signed 64.64-bit fixed point number may have. 
   */
  int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

  /*
   * Maximum value signed 64.64-bit fixed point number may have. 
   */
  int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

  /**
   * Convert signed 256-bit integer number into signed 64.64-bit fixed point
   * number.  Revert on overflow.
   *
   * @param x signed 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function fromInt (int256 x) internal pure returns (int128) {
    unchecked {
      require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);
      return int128 (x << 64);
    }
  }

  /**
   * Convert signed 64.64 fixed point number into signed 64-bit integer number
   * rounding down.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64-bit integer number
   */
  function toInt (int128 x) internal pure returns (int64) {
    unchecked {
      return int64 (x >> 64);
    }
  }

  /**
   * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
   * number.  Revert on overflow.
   *
   * @param x unsigned 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function fromUInt (uint256 x) internal pure returns (int128) {
    unchecked {
      require (x <= 0x7FFFFFFFFFFFFFFF);
      return int128 (int256 (x << 64));
    }
  }

  /**
   * Convert signed 64.64 fixed point number into unsigned 64-bit integer
   * number rounding down.  Revert on underflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return unsigned 64-bit integer number
   */
  function toUInt (int128 x) internal pure returns (uint64) {
    unchecked {
      require (x >= 0);
      return uint64 (uint128 (x >> 64));
    }
  }

  /**
   * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point
   * number rounding down.  Revert on overflow.
   *
   * @param x signed 128.128-bin fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function from128x128 (int256 x) internal pure returns (int128) {
    unchecked {
      int256 result = x >> 64;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Convert signed 64.64 fixed point number into signed 128.128 fixed point
   * number.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 128.128 fixed point number
   */
  function to128x128 (int128 x) internal pure returns (int256) {
    unchecked {
      return int256 (x) << 64;
    }
  }

  /**
   * Calculate x + y.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function add (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) + y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x - y.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function sub (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) - y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x * y rounding down.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function mul (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) * y >> 64;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point
   * number and y is signed 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64 fixed point number
   * @param y signed 256-bit integer number
   * @return signed 256-bit integer number
   */
  function muli (int128 x, int256 y) internal pure returns (int256) {
    unchecked {
      if (x == MIN_64x64) {
        require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
          y <= 0x1000000000000000000000000000000000000000000000000);
        return -y << 63;
      } else {
        bool negativeResult = false;
        if (x < 0) {
          x = -x;
          negativeResult = true;
        }
        if (y < 0) {
          y = -y; // We rely on overflow behavior here
          negativeResult = !negativeResult;
        }
        uint256 absoluteResult = mulu (x, uint256 (y));
        if (negativeResult) {
          require (absoluteResult <=
            0x8000000000000000000000000000000000000000000000000000000000000000);
          return -int256 (absoluteResult); // We rely on overflow behavior here
        } else {
          require (absoluteResult <=
            0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
          return int256 (absoluteResult);
        }
      }
    }
  }

  /**
   * Calculate x * y rounding down, where x is signed 64.64 fixed point number
   * and y is unsigned 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64 fixed point number
   * @param y unsigned 256-bit integer number
   * @return unsigned 256-bit integer number
   */
  function mulu (int128 x, uint256 y) internal pure returns (uint256) {
    unchecked {
      if (y == 0) return 0;

      require (x >= 0);

      uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;
      uint256 hi = uint256 (int256 (x)) * (y >> 128);

      require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
      hi <<= 64;

      require (hi <=
        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);
      return hi + lo;
    }
  }

  /**
   * Calculate x / y rounding towards zero.  Revert on overflow or when y is
   * zero.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function div (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);
      int256 result = (int256 (x) << 64) / y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are signed 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x signed 256-bit integer number
   * @param y signed 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function divi (int256 x, int256 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);

      bool negativeResult = false;
      if (x < 0) {
        x = -x; // We rely on overflow behavior here
        negativeResult = true;
      }
      if (y < 0) {
        y = -y; // We rely on overflow behavior here
        negativeResult = !negativeResult;
      }
      uint128 absoluteResult = divuu (uint256 (x), uint256 (y));
      if (negativeResult) {
        require (absoluteResult <= 0x80000000000000000000000000000000);
        return -int128 (absoluteResult); // We rely on overflow behavior here
      } else {
        require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
        return int128 (absoluteResult); // We rely on overflow behavior here
      }
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x unsigned 256-bit integer number
   * @param y unsigned 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function divu (uint256 x, uint256 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);
      uint128 result = divuu (x, y);
      require (result <= uint128 (MAX_64x64));
      return int128 (result);
    }
  }

  /**
   * Calculate -x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function neg (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != MIN_64x64);
      return -x;
    }
  }

  /**
   * Calculate |x|.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function abs (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != MIN_64x64);
      return x < 0 ? -x : x;
    }
  }

  /**
   * Calculate 1 / x rounding towards zero.  Revert on overflow or when x is
   * zero.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function inv (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != 0);
      int256 result = int256 (0x100000000000000000000000000000000) / x;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function avg (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      return int128 ((int256 (x) + int256 (y)) >> 1);
    }
  }

  /**
   * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.
   * Revert on overflow or in case x * y is negative.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function gavg (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 m = int256 (x) * int256 (y);
      require (m >= 0);
      require (m <
          0x4000000000000000000000000000000000000000000000000000000000000000);
      return int128 (sqrtu (uint256 (m)));
    }
  }

  /**
   * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number
   * and y is unsigned 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y uint256 value
   * @return signed 64.64-bit fixed point number
   */
  function pow (int128 x, uint256 y) internal pure returns (int128) {
    unchecked {
      bool negative = x < 0 && y & 1 == 1;

      uint256 absX = uint128 (x < 0 ? -x : x);
      uint256 absResult;
      absResult = 0x100000000000000000000000000000000;

      if (absX <= 0x10000000000000000) {
        absX <<= 63;
        while (y != 0) {
          if (y & 0x1 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x2 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x4 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x8 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          y >>= 4;
        }

        absResult >>= 64;
      } else {
        uint256 absXShift = 63;
        if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; }
        if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; }
        if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; }
        if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; }
        if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; }
        if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; }

        uint256 resultShift = 0;
        while (y != 0) {
          require (absXShift < 64);

          if (y & 0x1 != 0) {
            absResult = absResult * absX >> 127;
            resultShift += absXShift;
            if (absResult > 0x100000000000000000000000000000000) {
              absResult >>= 1;
              resultShift += 1;
            }
          }
          absX = absX * absX >> 127;
          absXShift <<= 1;
          if (absX >= 0x100000000000000000000000000000000) {
              absX >>= 1;
              absXShift += 1;
          }

          y >>= 1;
        }

        require (resultShift < 64);
        absResult >>= 64 - resultShift;
      }
      int256 result = negative ? -int256 (absResult) : int256 (absResult);
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate sqrt (x) rounding down.  Revert if x < 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function sqrt (int128 x) internal pure returns (int128) {
    unchecked {
      require (x >= 0);
      return int128 (sqrtu (uint256 (int256 (x)) << 64));
    }
  }

  /**
   * Calculate binary logarithm of x.  Revert if x <= 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function log_2 (int128 x) internal pure returns (int128) {
    unchecked {
      require (x > 0);

      int256 msb = 0;
      int256 xc = x;
      if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }
      if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
      if (xc >= 0x10000) { xc >>= 16; msb += 16; }
      if (xc >= 0x100) { xc >>= 8; msb += 8; }
      if (xc >= 0x10) { xc >>= 4; msb += 4; }
      if (xc >= 0x4) { xc >>= 2; msb += 2; }
      if (xc >= 0x2) msb += 1;  // No need to shift xc anymore

      int256 result = msb - 64 << 64;
      uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb);
      for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
        ux *= ux;
        uint256 b = ux >> 255;
        ux >>= 127 + b;
        result += bit * int256 (b);
      }

      return int128 (result);
    }
  }

  /**
   * Calculate natural logarithm of x.  Revert if x <= 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function ln (int128 x) internal pure returns (int128) {
    unchecked {
      require (x > 0);

      return int128 (int256 (
          uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128));
    }
  }

  /**
   * Calculate binary exponent of x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function exp_2 (int128 x) internal pure returns (int128) {
    unchecked {
      require (x < 0x400000000000000000); // Overflow

      if (x < -0x400000000000000000) return 0; // Underflow

      uint256 result = 0x80000000000000000000000000000000;

      if (x & 0x8000000000000000 > 0)
        result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;
      if (x & 0x4000000000000000 > 0)
        result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;
      if (x & 0x2000000000000000 > 0)
        result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;
      if (x & 0x1000000000000000 > 0)
        result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;
      if (x & 0x800000000000000 > 0)
        result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;
      if (x & 0x400000000000000 > 0)
        result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;
      if (x & 0x200000000000000 > 0)
        result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;
      if (x & 0x100000000000000 > 0)
        result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;
      if (x & 0x80000000000000 > 0)
        result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;
      if (x & 0x40000000000000 > 0)
        result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;
      if (x & 0x20000000000000 > 0)
        result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;
      if (x & 0x10000000000000 > 0)
        result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;
      if (x & 0x8000000000000 > 0)
        result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;
      if (x & 0x4000000000000 > 0)
        result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;
      if (x & 0x2000000000000 > 0)
        result = result * 0x1000162E525EE054754457D5995292026 >> 128;
      if (x & 0x1000000000000 > 0)
        result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;
      if (x & 0x800000000000 > 0)
        result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;
      if (x & 0x400000000000 > 0)
        result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;
      if (x & 0x200000000000 > 0)
        result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;
      if (x & 0x100000000000 > 0)
        result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;
      if (x & 0x80000000000 > 0)
        result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;
      if (x & 0x40000000000 > 0)
        result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;
      if (x & 0x20000000000 > 0)
        result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;
      if (x & 0x10000000000 > 0)
        result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;
      if (x & 0x8000000000 > 0)
        result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;
      if (x & 0x4000000000 > 0)
        result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;
      if (x & 0x2000000000 > 0)
        result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;
      if (x & 0x1000000000 > 0)
        result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;
      if (x & 0x800000000 > 0)
        result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;
      if (x & 0x400000000 > 0)
        result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;
      if (x & 0x200000000 > 0)
        result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;
      if (x & 0x100000000 > 0)
        result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;
      if (x & 0x80000000 > 0)
        result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;
      if (x & 0x40000000 > 0)
        result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;
      if (x & 0x20000000 > 0)
        result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;
      if (x & 0x10000000 > 0)
        result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;
      if (x & 0x8000000 > 0)
        result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;
      if (x & 0x4000000 > 0)
        result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;
      if (x & 0x2000000 > 0)
        result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;
      if (x & 0x1000000 > 0)
        result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;
      if (x & 0x800000 > 0)
        result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;
      if (x & 0x400000 > 0)
        result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;
      if (x & 0x200000 > 0)
        result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;
      if (x & 0x100000 > 0)
        result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;
      if (x & 0x80000 > 0)
        result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;
      if (x & 0x40000 > 0)
        result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;
      if (x & 0x20000 > 0)
        result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;
      if (x & 0x10000 > 0)
        result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;
      if (x & 0x8000 > 0)
        result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;
      if (x & 0x4000 > 0)
        result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;
      if (x & 0x2000 > 0)
        result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;
      if (x & 0x1000 > 0)
        result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;
      if (x & 0x800 > 0)
        result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;
      if (x & 0x400 > 0)
        result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;
      if (x & 0x200 > 0)
        result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;
      if (x & 0x100 > 0)
        result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;
      if (x & 0x80 > 0)
        result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;
      if (x & 0x40 > 0)
        result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;
      if (x & 0x20 > 0)
        result = result * 0x100000000000000162E42FEFA39EF366F >> 128;
      if (x & 0x10 > 0)
        result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;
      if (x & 0x8 > 0)
        result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;
      if (x & 0x4 > 0)
        result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;
      if (x & 0x2 > 0)
        result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;
      if (x & 0x1 > 0)
        result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;

      result >>= uint256 (int256 (63 - (x >> 64)));
      require (result <= uint256 (int256 (MAX_64x64)));

      return int128 (int256 (result));
    }
  }

  /**
   * Calculate natural exponent of x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function exp (int128 x) internal pure returns (int128) {
    unchecked {
      require (x < 0x400000000000000000); // Overflow

      if (x < -0x400000000000000000) return 0; // Underflow

      return exp_2 (
          int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x unsigned 256-bit integer number
   * @param y unsigned 256-bit integer number
   * @return unsigned 64.64-bit fixed point number
   */
  function divuu (uint256 x, uint256 y) private pure returns (uint128) {
    unchecked {
      require (y != 0);

      uint256 result;

      if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
        result = (x << 64) / y;
      else {
        uint256 msb = 192;
        uint256 xc = x >> 192;
        if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
        if (xc >= 0x10000) { xc >>= 16; msb += 16; }
        if (xc >= 0x100) { xc >>= 8; msb += 8; }
        if (xc >= 0x10) { xc >>= 4; msb += 4; }
        if (xc >= 0x4) { xc >>= 2; msb += 2; }
        if (xc >= 0x2) msb += 1;  // No need to shift xc anymore

        result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);
        require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

        uint256 hi = result * (y >> 128);
        uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

        uint256 xh = x >> 192;
        uint256 xl = x << 64;

        if (xl < lo) xh -= 1;
        xl -= lo; // We rely on overflow behavior here
        lo = hi << 128;
        if (xl < lo) xh -= 1;
        xl -= lo; // We rely on overflow behavior here

        assert (xh == hi >> 128);

        result += xl / y;
      }

      require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
      return uint128 (result);
    }
  }

  /**
   * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer
   * number.
   *
   * @param x unsigned 256-bit integer number
   * @return unsigned 128-bit integer number
   */
  function sqrtu (uint256 x) private pure returns (uint128) {
    unchecked {
      if (x == 0) return 0;
      else {
        uint256 xx = x;
        uint256 r = 1;
        if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; }
        if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; }
        if (xx >= 0x100000000) { xx >>= 32; r <<= 16; }
        if (xx >= 0x10000) { xx >>= 16; r <<= 8; }
        if (xx >= 0x100) { xx >>= 8; r <<= 4; }
        if (xx >= 0x10) { xx >>= 4; r <<= 2; }
        if (xx >= 0x8) { r <<= 1; }
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1; // Seven iterations should be enough
        uint256 r1 = x / r;
        return uint128 (r < r1 ? r : r1);
      }
    }
  }
}

File 10 of 28 : FundImplementation.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {AssetModule} from "./modules/AssetModule.sol";
import {ExecutionModule} from "./modules/ExecutionModule.sol";
import {ManagementFeeModule} from "./modules/ManagementFeeModule.sol";
import {PerformanceFeeModule} from "./modules/PerformanceFeeModule.sol";
import {ShareModule} from "./modules/ShareModule.sol";
import {IAssetRouter} from "./assets/interfaces/IAssetRouter.sol";
import {IComptroller} from "./interfaces/IComptroller.sol";
import {IShareToken} from "./interfaces/IShareToken.sol";
import {Errors} from "./utils/Errors.sol";

/// @title The implementation contract for fund
/// @notice The functions that requires ownership, interaction between
///         different modules should be overridden and implemented here.
contract FundImplementation is AssetModule, ShareModule, ExecutionModule, ManagementFeeModule, PerformanceFeeModule {
    using SafeERC20 for IERC20;
    using SafeCast for uint256;

    constructor() {
        // set owner to address(0) in implementation contract
        renounceOwnership();
    }

    /////////////////////////////////////////////////////
    // State Changes
    /////////////////////////////////////////////////////
    /// @notice Initializer, only in `Initializing` state.
    /// @param level_ The tier of the fund.
    /// @param comptroller_ The comptroller address.
    /// @param denomination_ The denomination asset.
    /// @param shareToken_ The share token address.
    /// @param mFeeRate_ The management fee rate.
    /// @param pFeeRate_ The performance fee rate.
    /// @param crystallizationPeriod_ The crystallization period.
    /// @param newOwner_ The owner to be assigned to the fund.
    function initialize(
        uint256 level_,
        IComptroller comptroller_,
        IERC20 denomination_,
        IShareToken shareToken_,
        uint256 mFeeRate_,
        uint256 pFeeRate_,
        uint256 crystallizationPeriod_,
        address newOwner_
    ) external whenState(State.Initializing) {
        _setLevel(level_);
        _setComptroller(comptroller_);
        _setDenomination(denomination_);
        _setShareToken(shareToken_);
        _setManagementFeeRate(mFeeRate_);
        _setPerformanceFeeRate(pFeeRate_);
        _setCrystallizationPeriod(crystallizationPeriod_);
        _setVault(comptroller_.dsProxyRegistry());
        _setMortgageVault(comptroller_);
        _transferOwnership(newOwner_);
        _review();
    }

    /// @notice Finalize the initialization of the fund.
    function finalize() external nonReentrant onlyOwner {
        _finalize();

        // Add denomination to list and never remove
        Errors._require(getAssetList().length == 0, Errors.Code.IMPLEMENTATION_ASSET_LIST_NOT_EMPTY);

        Errors._require(
            comptroller.isValidDenomination(address(denomination)),
            Errors.Code.IMPLEMENTATION_INVALID_DENOMINATION
        );
        _addAsset(address(denomination));

        // Set approval for investor to redeem
        _setVaultApproval(comptroller.setupAction());

        // Initialize management fee parameters
        _initializeManagementFee();

        // Initialize performance fee parameters
        _initializePerformanceFee();

        // Transfer mortgage token to this fund then call mortgage vault
        (bool isMortgageTierSet, uint256 amount) = comptroller.mortgageTier(level);
        Errors._require(isMortgageTierSet, Errors.Code.IMPLEMENTATION_INVALID_MORTGAGE_TIER);
        if (amount > 0) {
            IERC20 mortgageToken = mortgageVault.mortgageToken();
            mortgageToken.safeTransferFrom(msg.sender, address(this), amount);
            mortgageToken.safeApprove(address(mortgageVault), amount);
            mortgageVault.mortgage(amount);
        }
    }

    /// @notice Resume the fund by anyone if can settle pending share.
    /// @dev Resume only in `Pending` state.
    function resume() external nonReentrant whenState(State.Pending) {
        uint256 grossAssetValue = getGrossAssetValue();
        Errors._require(
            _isPendingResolvable(true, grossAssetValue),
            Errors.Code.IMPLEMENTATION_PENDING_SHARE_NOT_RESOLVABLE
        );
        _settlePendingShare(true);
        _resume();
    }

    /// @notice Liquidate the fund by anyone and transfer owner to liquidator.
    function liquidate() external nonReentrant {
        Errors._require(pendingStartTime != 0, Errors.Code.IMPLEMENTATION_PENDING_NOT_START);
        Errors._require(
            block.timestamp >= pendingStartTime + comptroller.pendingExpiration(),
            Errors.Code.IMPLEMENTATION_PENDING_NOT_EXPIRE
        );
        _crystallize();
        _liquidate();

        _transferOwnership(comptroller.pendingLiquidator());
    }

    /// @notice Close fund. The pending share will be settled without penalty.
    /// @dev This function can only be used in `Executing` and `Liquidating` states.
    /// @inheritdoc AssetModule
    function close() public override onlyOwner nonReentrant whenStates(State.Executing, State.Liquidating) {
        _settlePendingShare(false);
        if (state == State.Executing) {
            _crystallize();
        }
        super.close();

        mortgageVault.claim(msg.sender);
    }

    /////////////////////////////////////////////////////
    // Setters
    /////////////////////////////////////////////////////
    /// @notice Set management fee rate only in `Reviewing` state.
    /// @param mFeeRate_ The management fee rate on a 1e4 basis.
    function setManagementFeeRate(uint256 mFeeRate_) external onlyOwner whenState(State.Reviewing) {
        _setManagementFeeRate(mFeeRate_);
    }

    /// @notice Set performance fee rate only in `Reviewing` state.
    /// @param pFeeRate_ The performance fee rate on a 1e4 basis.
    function setPerformanceFeeRate(uint256 pFeeRate_) external onlyOwner whenState(State.Reviewing) {
        _setPerformanceFeeRate(pFeeRate_);
    }

    /// @notice Set crystallization period only in `Reviewing` state.
    /// @param crystallizationPeriod_ The crystallization period to be set in second.
    function setCrystallizationPeriod(uint256 crystallizationPeriod_) external onlyOwner whenState(State.Reviewing) {
        _setCrystallizationPeriod(crystallizationPeriod_);
    }

    /////////////////////////////////////////////////////
    // Getters
    /////////////////////////////////////////////////////
    /// @notice Get gross asset value.
    /// @return Convert value to denomination amount.
    function getGrossAssetValue() public view virtual returns (uint256) {
        address[] memory assets = getAssetList();
        uint256 length = assets.length;
        uint256[] memory amounts = new uint256[](length);
        for (uint256 i = 0; i < length; i++) {
            amounts[i] = IERC20(assets[i]).balanceOf(address(vault));
        }

        return _getAssetRouter().calcAssetsTotalValue(assets, amounts, address(denomination));
    }

    /// @notice Get the value of a give asset.
    /// @param asset_ The asset to be queried.
    function getAssetValue(address asset_) public view returns (int256) {
        uint256 balance = IERC20(asset_).balanceOf(address(vault));
        if (balance == 0) return 0;

        return _getAssetRouter().calcAssetValue(asset_, balance, address(denomination));
    }

    function _getAssetRouter() internal view returns (IAssetRouter) {
        return comptroller.assetRouter();
    }

    /// inheritdoc ShareModule, PerformanceFeeModule.
    function __getGrossAssetValue() internal view override(ShareModule, PerformanceFeeModule) returns (uint256) {
        return getGrossAssetValue();
    }

    /// @notice Get the balance of the denomination asset.
    /// @return The balance of reserve.
    /// @inheritdoc ShareModule
    function getReserve() public view override returns (uint256) {
        return denomination.balanceOf(address(vault));
    }

    /////////////////////////////////////////////////////
    // Asset Module
    /////////////////////////////////////////////////////
    /// @notice Add the asset to the tracking list by owner.
    /// @param asset_ The asset to be added.
    function addAsset(address asset_) external nonReentrant onlyOwner {
        _addAsset(asset_);
        _checkAssetCapacity();
    }

    /// @notice Add the asset to the tracking list.
    /// @param asset_ The asset to be added.
    /// @inheritdoc AssetModule
    function _addAsset(address asset_) internal override {
        Errors._require(comptroller.isValidDealingAsset(level, asset_), Errors.Code.IMPLEMENTATION_INVALID_ASSET);

        address _denomination = address(denomination);
        if (asset_ == _denomination) {
            super._addAsset(asset_);
        } else {
            int256 value = getAssetValue(asset_);
            int256 dust = _getDenominationDust(_denomination);

            if (value >= dust || value < 0) {
                super._addAsset(asset_);
            }
        }
    }

    /// @notice Remove the asset from the tracking list by owner.
    /// @param asset_ The asset to be removed.
    function removeAsset(address asset_) external nonReentrant onlyOwner {
        _removeAsset(asset_);
    }

    /// @notice Remove the asset from the tracking list.
    /// @param asset_ The asset to be removed.
    /// @inheritdoc AssetModule
    function _removeAsset(address asset_) internal override {
        // Do not allow to remove denomination from list
        address _denomination = address(denomination);
        if (asset_ != _denomination) {
            int256 value = getAssetValue(asset_);
            int256 dust = _getDenominationDust(_denomination);

            if (value < dust && value >= 0) {
                super._removeAsset(asset_);
            }
        }
    }

    /// @notice Get the denomination dust.
    /// @param denomination_ The denomination address.
    /// @return The dust of denomination.
    function _getDenominationDust(address denomination_) internal view returns (int256) {
        return comptroller.getDenominationDust(denomination_).toInt256();
    }

    /////////////////////////////////////////////////////
    // Execution module
    /////////////////////////////////////////////////////
    /// @inheritdoc ExecutionModule
    function execute(bytes calldata data_) public override nonReentrant onlyOwner {
        super.execute(data_);
    }

    /// @notice Check if the gross asset value is more than gross asset value tolerance after execute.
    function _isAfterValueEnough(uint256 prevAssetValue_, uint256 grossAssetValue_) internal view returns (bool) {
        uint256 minGrossAssetValue = (prevAssetValue_ * comptroller.execAssetValueToleranceRate()) /
            _FUND_PERCENTAGE_BASE;

        return grossAssetValue_ >= minGrossAssetValue;
    }

    /// @notice Execute an action on the fund's behalf.
    /// @return The gross asset value.
    /// @inheritdoc ExecutionModule
    function _beforeExecute() internal virtual override returns (uint256) {
        return getGrossAssetValue();
    }

    /// @notice Check the reserve after the execution.
    /// @return The gross asset value.
    /// @inheritdoc ExecutionModule
    function _afterExecute(bytes memory response_, uint256 prevGrossAssetValue_) internal override returns (uint256) {
        // Remove asset from assetList
        address[] memory assetList = getAssetList();
        for (uint256 i = 0; i < assetList.length; ++i) {
            _removeAsset(assetList[i]);
        }

        // Add new asset to assetList
        address[] memory dealingAssets = abi.decode(response_, (address[]));
        for (uint256 i = 0; i < dealingAssets.length; ++i) {
            _addAsset(dealingAssets[i]);
        }

        _checkAssetCapacity();

        // Get new gross asset value
        uint256 grossAssetValue = getGrossAssetValue();
        Errors._require(
            _isAfterValueEnough(prevGrossAssetValue_, grossAssetValue),
            Errors.Code.IMPLEMENTATION_INSUFFICIENT_TOTAL_VALUE_FOR_EXECUTION
        );

        // Resume fund if the balance is sufficient to resolve pending state
        if (state == State.Pending && _isPendingResolvable(true, grossAssetValue)) {
            uint256 totalRedemption = _settlePendingShare(true);
            _resume();
            // minus redeemed denomination amount
            grossAssetValue -= totalRedemption;
        }

        return grossAssetValue;
    }

    /////////////////////////////////////////////////////
    // Management fee module
    /////////////////////////////////////////////////////
    /// @notice Manangement fee should only be accumulated in `Executing` state.
    /// @return The newly minted shares.
    /// @inheritdoc ManagementFeeModule
    function _updateManagementFee() internal override returns (uint256) {
        if (state == State.Executing) {
            return super._updateManagementFee();
        } else if (state == State.Pending) {
            lastMFeeClaimTime = block.timestamp;
        }
        return 0;
    }

    /////////////////////////////////////////////////////
    // Performance fee module
    /////////////////////////////////////////////////////
    /// @notice Crystallize for the performance fee.
    /// @dev This function can only be used in `Executing` and `Pending` states.
    /// @inheritdoc PerformanceFeeModule
    function crystallize()
        public
        override
        nonReentrant
        onlyOwner
        whenStates(State.Executing, State.Pending)
        returns (uint256)
    {
        return super.crystallize();
    }

    /// @notice Update the performace fee.
    /// @dev This function works only in `Executing` and `Pending` states.
    /// @inheritdoc PerformanceFeeModule
    function _updatePerformanceFee(uint256 grossAssetValue_) internal override {
        if (state == State.Executing || state == State.Pending) {
            super._updatePerformanceFee(grossAssetValue_);
        }
    }

    /// @notice Update the management fee before crystallization.
    /// @return The performance fee amount to be claimed.
    /// @inheritdoc PerformanceFeeModule
    function _crystallize() internal override returns (uint256) {
        _updateManagementFee();
        return super._crystallize();
    }

    /////////////////////////////////////////////////////
    // Share module
    /////////////////////////////////////////////////////
    /// @notice Update the management fee and performance fee before purchase
    ///         to get the lastest share price.
    /// @return The gross asset value.
    /// @inheritdoc ShareModule
    function _beforePurchase() internal override returns (uint256) {
        uint256 grossAssetValue = getGrossAssetValue();
        _updateManagementFee();
        _updatePerformanceFee(grossAssetValue);
        return grossAssetValue;
    }

    /// @notice Update the gross share price after purchase.
    /// @dev Attempt to settle in `Pending` state and resume to `Executing` state
    ///      if the fund is resolvable.
    /// @inheritdoc ShareModule
    function _afterPurchase(uint256 grossAssetValue_) internal override {
        _updateGrossSharePrice(grossAssetValue_);
        if (state == State.Pending && _isPendingResolvable(true, grossAssetValue_)) {
            grossAssetValue_ -= _settlePendingShare(true);
            _updateGrossSharePrice(grossAssetValue_);
            _resume();
        }
        return;
    }

    /// @notice Update the management fee and performance fee before redeem
    ///         to get the latest share price.
    /// @return The gross asset value.
    /// @inheritdoc ShareModule
    function _beforeRedeem() internal override returns (uint256) {
        uint256 grossAssetValue = getGrossAssetValue();
        _updateManagementFee();
        _updatePerformanceFee(grossAssetValue);
        return grossAssetValue;
    }

    /// @notice Update the gross share price after redeem.
    /// @inheritdoc ShareModule
    function _afterRedeem(uint256 grossAssetValue_) internal override {
        _updateGrossSharePrice(grossAssetValue_);
        return;
    }
}

File 11 of 28 : FundProxyStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {LibUniqueAddressList} from "./libraries/LibUniqueAddressList.sol";
import {IComptroller} from "./interfaces/IComptroller.sol";
import {IDSProxy} from "./interfaces/IDSProxy.sol";
import {IShareToken} from "./interfaces/IShareToken.sol";
import {IMortgageVault} from "./interfaces/IMortgageVault.sol";

/// @title Furucombo fund proxy storage
/// @dev This is the first version of the storage layout which must be consistent after add new states.
abstract contract FundProxyStorageV1 is Ownable, ReentrancyGuard {
    /// Fund States
    /// Initializing - The initial state of a newly created fund, set the basic parameters of Fund.
    /// Reviewing - After initialization, only the fee parameter can be adjusted.
    /// Executing - Normal operation, when the remaining amount of denomination is positive.
    /// Pending - Unable to fulfill redemption will enter pending state. When the purchase amount is
    ///           sufficient or the strategy is executed to settle the debt, it will resume to Executing state.
    /// Liquidating - When the fund stays in pending state over pendingExpiration, it enters liquidation
    ///               process. The fund will be transferred to the liquidator, who is responsible for
    ///               exchanging assets back to denomination tokens.
    /// Closed - When only denomination tokens are left, the fund can be closed and the investors can
    ///          redeem their share token.
    enum State {
        Initializing,
        Reviewing,
        Executing,
        Pending,
        Liquidating,
        Closed
    }

    /// Pending user info stores the settled share tokens per round.
    struct PendingUserInfo {
        uint256 pendingRound;
        uint256 pendingShare;
    }

    /// Pending round info stored the total pending share and total redemption amount.
    struct PendingRoundInfo {
        uint256 totalPendingShare;
        uint256 totalRedemption;
    }

    /// @notice The level of this fund.
    uint256 public level;

    /// @notice The timestamp of entering pending state.
    uint256 public pendingStartTime;

    /// @notice The current state of the fund.
    State public state;

    /// @notice The comptroller of the fund.
    IComptroller public comptroller;

    /// @notice The denomination token for this fund.
    IERC20 public denomination;

    /// @notice The fund share token contract.
    IShareToken public shareToken;

    /// @notice The contract used to store fund assets.
    IDSProxy public vault;

    /// @notice The mortgage vault of this fund.
    IMortgageVault public mortgageVault;

    /// @notice The asset list currently managed by fund.
    LibUniqueAddressList.List internal _assetList;

    /// @notice The current total pending share amount.
    uint256 public currentTotalPendingShare;

    /// @notice The current total bunus share token amount.
    uint256 public currentTotalPendingBonus;

    /// @notice The pending round info list.
    PendingRoundInfo[] public pendingRoundList;

    /// @notice The pending info list of each pending user.
    mapping(address => PendingUserInfo) public pendingUsers;

    /// @notice The last timestamp of claiming management fee.
    uint256 public lastMFeeClaimTime;

    /// @notice The management fee rate, should be a floating point number.
    int128 public mFeeRate64x64;

    /// @notice The high water mark, should be a floating point number.
    int128 public hwm64x64;

    /// @notice The last gross share price, should be a floating point number.
    int128 public lastGrossSharePrice64x64;

    /// @notice The performance fee rate, should be a floating point number.
    int128 public pFeeRate64x64;

    /// @notice The sum of performance fee.
    uint256 public pFeeSum;

    /// @notice The last outstanding share amount.
    uint256 public lastOutstandingShare;

    /// @notice The timestamp of starting crystallization.
    uint256 public crystallizationStart;

    /// @notice The crystallization period to be set in second.
    uint256 public crystallizationPeriod;

    /// @notice The last crystallization timestamp.
    uint256 public lastCrystallization;
}

abstract contract FundProxyStorage is FundProxyStorageV1 {}

File 12 of 28 : FundProxyStorageUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Errors} from "./utils/Errors.sol";
import {FundProxyStorage} from "./FundProxyStorage.sol";
import {IComptroller} from "./interfaces/IComptroller.sol";
import {IDSProxy, IDSProxyRegistry} from "./interfaces/IDSProxy.sol";
import {IShareToken} from "./interfaces/IShareToken.sol";
import {ISetupAction} from "./interfaces/ISetupAction.sol";

/// @title Furucombo fund proxy storage utility
abstract contract FundProxyStorageUtils is FundProxyStorage {
    uint256 internal constant _FUND_PERCENTAGE_BASE = 1e4;

    event StateTransited(State to);

    error InvalidState(State current);

    /// @dev Prevent functions from being called outside of this state.
    modifier whenState(State expect_) {
        _whenState(expect_);
        _;
    }

    /// @dev Prevent functions from being called outside of these two states.
    modifier whenStates(State expect1_, State expect2_) {
        _whenStates(expect1_, expect2_);
        _;
    }

    /// @dev Prevent functions from being called outside of these three states.
    modifier when3States(
        State expect1_,
        State expect2_,
        State expect3_
    ) {
        _when3States(expect1_, expect2_, expect3_);
        _;
    }

    /// @dev Prevent the function from being called in this state.
    modifier whenNotState(State expectNot_) {
        _whenNotState(expectNot_);
        _;
    }

    /// @dev Prevent functions from being called outside of this state.
    function _whenState(State expect_) internal view {
        if (state != expect_) revert InvalidState(state);
    }

    /// @dev Prevent functions from being called outside of this two state.
    function _whenStates(State expect1_, State expect2_) internal view {
        State s = state;
        if (s != expect1_ && s != expect2_) revert InvalidState(s);
    }

    /// @dev Prevent functions from being called outside of this three state.
    function _when3States(
        State expect1_,
        State expect2_,
        State expect3_
    ) internal view {
        State s = state;
        if (s != expect1_ && s != expect2_ && s != expect3_) revert InvalidState(s);
    }

    /// @dev Prevent the function from being called in this state.
    function _whenNotState(State expectNot_) internal view {
        if (state == expectNot_) revert InvalidState(state);
    }

    /// @dev Enter `Reviewing` state from `Initializing` state only.
    function _review() internal whenState(State.Initializing) {
        _enterState(State.Reviewing);
    }

    /// @dev Enter `Executing` state from `Reviewing` state only.
    function _finalize() internal whenState(State.Reviewing) {
        _enterState(State.Executing);
    }

    /// @dev Enter `Pending` state from `Executing` state only.
    function _pend() internal whenState(State.Executing) {
        _enterState(State.Pending);
        pendingStartTime = block.timestamp;
    }

    /// @dev Enter `Executing` state from `Pending` state only.
    function _resume() internal whenState(State.Pending) {
        pendingStartTime = 0;
        _enterState(State.Executing);
    }

    /// @dev Enter `Liquidating` state from `Pending` state only.
    function _liquidate() internal whenState(State.Pending) {
        pendingStartTime = 0;
        _enterState(State.Liquidating);
    }

    /// @dev Enter `Closed` state from `Executing` and `Liquidating` states only.
    function _close() internal whenStates(State.Executing, State.Liquidating) {
        _enterState(State.Closed);
    }

    /// @dev Change the fund state and emit state transited event.
    function _enterState(State state_) internal {
        state = state_;
        emit StateTransited(state_);
    }

    /////////////////////////////////////////////////////
    // Setters
    /////////////////////////////////////////////////////
    /// @notice Set the tier of the fund.
    function _setLevel(uint256 level_) internal {
        _checkZero(level);
        _checkNotZero(level_);
        level = level_;
    }

    /// @notice Set the comptroller of the fund.
    function _setComptroller(IComptroller comptroller_) internal {
        _checkZero(address(comptroller));
        _checkNotZero(address(comptroller_));
        comptroller = comptroller_;
    }

    /// @notice Set the denomination of the fund.
    function _setDenomination(IERC20 denomination_) internal {
        _checkZero(address(denomination));
        Errors._require(
            comptroller.isValidDenomination(address(denomination_)),
            Errors.Code.FUND_PROXY_STORAGE_UTILS_INVALID_DENOMINATION
        );
        denomination = denomination_;
    }

    /// @notice Set the share token of the fund.
    function _setShareToken(IShareToken shareToken_) internal {
        _checkZero(address(shareToken));
        _checkNotZero(address(shareToken_));
        shareToken = shareToken_;
    }

    /// @notice Set the mortgage vault of the fund.
    function _setMortgageVault(IComptroller comptroller_) internal {
        _checkZero(address(mortgageVault));
        mortgageVault = comptroller_.mortgageVault();
    }

    /// @notice Set the asset vault of the fund.
    function _setVault(IDSProxyRegistry dsProxyRegistry_) internal {
        _checkZero(address(vault));
        _checkNotZero(address(dsProxyRegistry_));

        // check if vault proxy exists
        IDSProxy vaultProxy = IDSProxy(dsProxyRegistry_.proxies(address(this)));
        if (address(vaultProxy) != address(0)) {
            Errors._require(vaultProxy.owner() == address(this), Errors.Code.FUND_PROXY_STORAGE_UTILS_UNKNOWN_OWNER);
            vault = vaultProxy;
        } else {
            // deploy vault
            vault = IDSProxy(dsProxyRegistry_.build());
            _checkNotZero(address(vault));
        }
    }

    /// @notice Set the vault approval.
    function _setVaultApproval(ISetupAction setupAction_) internal {
        _checkNotZero(address(vault));
        _checkNotZero(address(setupAction_));

        // set vault approval
        bytes memory data = abi.encodeWithSignature("maxApprove(address)", denomination);
        vault.execute(address(setupAction_), data);

        Errors._require(
            denomination.allowance(address(vault), address(this)) == type(uint256).max,
            Errors.Code.FUND_PROXY_STORAGE_UTILS_WRONG_ALLOWANCE
        );
    }

    /// @dev Check if the uint256 is zero.
    function _checkZero(uint256 param_) private pure {
        Errors._require(param_ == 0, Errors.Code.FUND_PROXY_STORAGE_UTILS_IS_NOT_ZERO);
    }

    /// @dev Check if the address is zero address.
    function _checkZero(address param_) private pure {
        Errors._require(param_ == address(0), Errors.Code.FUND_PROXY_STORAGE_UTILS_IS_NOT_ZERO);
    }

    /// @dev Check if the uint256 is not zero.
    function _checkNotZero(uint256 param_) private pure {
        Errors._require(param_ > 0, Errors.Code.FUND_PROXY_STORAGE_UTILS_IS_ZERO);
    }

    /// @dev Check if the address is not zero address.
    function _checkNotZero(address param_) private pure {
        Errors._require(param_ != address(0), Errors.Code.FUND_PROXY_STORAGE_UTILS_IS_ZERO);
    }
}

File 13 of 28 : IAssetOracle.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IAssetOracle {
    function calcConversionAmount(
        address base_,
        uint256 baseAmount_,
        address quote_
    ) external view returns (uint256);
}

File 14 of 28 : IAssetRegistry.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IAssetRegistry {
    function bannedResolvers(address) external view returns (bool);

    function register(address asset_, address resolver_) external;

    function unregister(address asset_) external;

    function banResolver(address resolver_) external;

    function unbanResolver(address resolver_) external;

    function resolvers(address asset_) external view returns (address);
}

File 15 of 28 : IAssetRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IAssetRegistry} from "./IAssetRegistry.sol";
import {IAssetOracle} from "./IAssetOracle.sol";

interface IAssetRouter {
    function oracle() external view returns (IAssetOracle);

    function registry() external view returns (IAssetRegistry);

    function setOracle(address oracle_) external;

    function setRegistry(address registry_) external;

    function calcAssetsTotalValue(
        address[] calldata bases_,
        uint256[] calldata amounts_,
        address quote_
    ) external view returns (uint256);

    function calcAssetValue(
        address asset_,
        uint256 amount_,
        address quote_
    ) external view returns (int256);
}

File 16 of 28 : IComptroller.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IAssetRouter} from "../assets/interfaces/IAssetRouter.sol";
import {IMortgageVault} from "./IMortgageVault.sol";
import {IDSProxyRegistry} from "./IDSProxy.sol";
import {ISetupAction} from "./ISetupAction.sol";

interface IComptroller {
    function owner() external view returns (address);

    function canDelegateCall(
        uint256 level_,
        address to_,
        bytes4 sig_
    ) external view returns (bool);

    function canContractCall(
        uint256 level_,
        address to_,
        bytes4 sig_
    ) external view returns (bool);

    function canHandlerCall(
        uint256 level_,
        address to_,
        bytes4 sig_
    ) external view returns (bool);

    function execFeePercentage() external view returns (uint256);

    function execFeeCollector() external view returns (address);

    function pendingLiquidator() external view returns (address);

    function pendingExpiration() external view returns (uint256);

    function execAssetValueToleranceRate() external view returns (uint256);

    function isValidDealingAsset(uint256 level_, address asset_) external view returns (bool);

    function isValidDealingAssets(uint256 level_, address[] calldata assets_) external view returns (bool);

    function isValidInitialAssets(uint256 level_, address[] calldata assets_) external view returns (bool);

    function assetCapacity() external view returns (uint256);

    function assetRouter() external view returns (IAssetRouter);

    function mortgageVault() external view returns (IMortgageVault);

    function pendingPenalty() external view returns (uint256);

    function execAction() external view returns (address);

    function mortgageTier(uint256 tier_) external view returns (bool, uint256);

    function isValidDenomination(address denomination_) external view returns (bool);

    function getDenominationDust(address denomination_) external view returns (uint256);

    function isValidCreator(address creator_) external view returns (bool);

    function dsProxyRegistry() external view returns (IDSProxyRegistry);

    function setupAction() external view returns (ISetupAction);
}

File 17 of 28 : IDSProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IDSProxy {
    function execute(address _target, bytes calldata _data) external payable returns (bytes memory response);

    function owner() external view returns (address);

    function setAuthority(address authority_) external;
}

interface IDSProxyFactory {
    function isProxy(address proxy) external view returns (bool);

    function build() external returns (address);

    function build(address owner) external returns (address);
}

interface IDSProxyRegistry {
    function proxies(address input) external view returns (address);

    function build() external returns (address);

    function build(address owner) external returns (address);
}

File 18 of 28 : IMortgageVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface IMortgageVault {
    function mortgageToken() external view returns (IERC20);

    function totalAmount() external view returns (uint256);

    function fundAmounts(address fund_) external view returns (uint256);

    function mortgage(uint256 amount_) external;

    function claim(address receiver_) external;
}

File 19 of 28 : ISetupAction.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface ISetupAction {
    function maxApprove(IERC20 token_) external;
}

File 20 of 28 : IShareToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";

interface IShareToken is IERC20, IERC20Permit {
    function mint(address account_, uint256 amount_) external;

    function burn(address account_, uint256 amount_) external;

    function move(
        address sender_,
        address recipient_,
        uint256 amount_
    ) external;

    function netTotalShare() external view returns (uint256);

    function grossTotalShare() external view returns (uint256);
}

File 21 of 28 : LibFee.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library LibFee {
    function _max64x64(int128 a_, int128 b_) internal pure returns (int128) {
        if (a_ > b_) {
            return a_;
        } else {
            return b_;
        }
    }

    function _max(int256 a_, int256 b_) internal pure returns (int256) {
        if (a_ > b_) {
            return a_;
        } else {
            return b_;
        }
    }

    function _max(uint256 a_, uint256 b_) internal pure returns (uint256) {
        if (a_ > b_) {
            return a_;
        } else {
            return b_;
        }
    }
}

File 22 of 28 : LibUniqueAddressList.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library LibUniqueAddressList {
    using LibUniqueAddressList for List;

    address private constant _HEAD = address(0);
    address private constant _TAIL = address(0);
    address private constant _NULL = address(0);

    struct List {
        uint256 sz;
        mapping(address => address) predecessor;
        mapping(address => address) successor;
    }

    // Getters
    function _get(List storage self_) internal view returns (address[] memory list) {
        if (self_._empty()) {
            return list;
        } else {
            list = new address[](self_.sz);
            uint256 index = 0;
            for (address p = self_._front(); p != _TAIL; p = self_._next(p)) {
                list[index] = p;
                index++;
            }
        }
    }

    function _empty(List storage self_) internal view returns (bool) {
        return (self_.sz == 0);
    }

    function _size(List storage self_) internal view returns (uint256) {
        return self_.sz;
    }

    function _exist(List storage self_, address node_) internal view returns (bool) {
        return (self_.successor[node_] != _NULL);
    }

    function _front(List storage self_) internal view returns (address) {
        return self_.successor[_HEAD];
    }

    function _back(List storage self_) internal view returns (address) {
        return self_.predecessor[_TAIL];
    }

    function _next(List storage self_, address node_) internal view returns (address) {
        address n = self_.successor[node_];
        return node_ == n ? _TAIL : n;
    }

    function _prev(List storage self_, address node_) internal view returns (address) {
        address p = self_.predecessor[node_];
        return node_ == p ? _HEAD : p;
    }

    function _pushFront(List storage self_, address node_) internal returns (bool) {
        if (self_._exist(node_)) {
            return false;
        } else {
            address f = self_._front();
            _connect(self_, _HEAD, node_);
            _connect(self_, node_, f);
            self_.sz++;
            return true;
        }
    }

    function _pushBack(List storage self_, address node_) internal returns (bool) {
        if (self_._exist(node_)) {
            return false;
        } else {
            address b = self_._back();
            _connect(self_, b, node_);
            _connect(self_, node_, _TAIL);
            self_.sz++;
            return true;
        }
    }

    function _popFront(List storage self_) internal returns (bool) {
        if (self_._empty()) {
            return false;
        } else {
            address f = self_._front();
            address newFront = self_._next(f);
            _connect(self_, _HEAD, newFront);
            _delete(self_, f);
            return true;
        }
    }

    function _popBack(List storage self_) internal returns (bool) {
        if (self_._empty()) {
            return false;
        } else {
            address b = self_._back();
            address newBack = self_._prev(b);
            _connect(self_, newBack, _TAIL);
            _delete(self_, b);
            return true;
        }
    }

    function _insert(
        List storage self_,
        address loc_,
        address node_
    ) internal returns (bool) {
        if (loc_ == _NULL || node_ == _NULL) {
            return false;
        } else if (!self_._exist(loc_)) {
            return false;
        } else if (self_._exist(node_)) {
            return false;
        } else {
            address p = self_._prev(loc_);
            _connect(self_, p, node_);
            _connect(self_, node_, loc_);
            self_.sz++;
            return true;
        }
    }

    function _remove(List storage self_, address node_) internal returns (bool) {
        if (node_ == _NULL) {
            return false;
        } else if (!self_._exist(node_)) {
            return false;
        } else {
            address p = self_._prev(node_);
            address n = self_._next(node_);
            _connect(self_, p, n);
            _delete(self_, node_);
            return true;
        }
    }

    function _connect(
        List storage self_,
        address node1_,
        address node2_
    ) private {
        self_.successor[node1_] = node2_ == _TAIL ? node1_ : node2_;
        self_.predecessor[node2_] = node1_ == _HEAD ? node2_ : node1_;
    }

    function _delete(List storage self_, address node_) private {
        self_.predecessor[node_] = _NULL;
        self_.successor[node_] = _NULL;
        self_.sz--;
    }
}

File 23 of 28 : AssetModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {FundProxyStorageUtils} from "../FundProxyStorageUtils.sol";
import {Errors} from "../utils/Errors.sol";
import {LibUniqueAddressList} from "../libraries/LibUniqueAddressList.sol";

/// @title Asset module
/// @notice Define the asset relate policy of the fund.
abstract contract AssetModule is FundProxyStorageUtils {
    using LibUniqueAddressList for LibUniqueAddressList.List;

    event AssetAdded(address asset);
    event AssetRemoved(address asset);

    /// @notice Get the permitted asset list.
    /// @return Return the permitted asset list array.
    function getAssetList() public view returns (address[] memory) {
        return _assetList._get();
    }

    /// @notice Check the remaining asset should be only the denomination asset
    /// when closing the vault.
    function close() public virtual {
        Errors._require(
            _assetList._size() == 1 && _assetList._front() == address(denomination),
            Errors.Code.ASSET_MODULE_DIFFERENT_ASSET_REMAINING
        );
        _close();
    }

    /// @notice Check asset capacity.
    function _checkAssetCapacity() internal view {
        Errors._require(
            getAssetList().length <= comptroller.assetCapacity(),
            Errors.Code.ASSET_MODULE_FULL_ASSET_CAPACITY
        );
    }

    /// @notice Add asset to the asset tracking list.
    /// @param asset_ The asset to be tracked.
    /// @dev This function can only be used in `Executing`, `Pending` and `Liquidating` states.
    function _addAsset(address asset_) internal virtual when3States(State.Executing, State.Pending, State.Liquidating) {
        if (_assetList._pushBack(asset_)) {
            emit AssetAdded(asset_);
        }
    }

    /// @notice Remove the asset from the asset tracking list.
    /// @dev This function can only be used in `Executing`, `Pending` and `Liquidating` states.
    function _removeAsset(address asset_)
        internal
        virtual
        when3States(State.Executing, State.Pending, State.Liquidating)
    {
        if (_assetList._remove(asset_)) {
            emit AssetRemoved(asset_);
        }
    }
}

File 24 of 28 : ExecutionModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {FundProxyStorageUtils} from "../FundProxyStorageUtils.sol";

/// @title Execution module
abstract contract ExecutionModule is FundProxyStorageUtils {
    event Executed();

    /// @notice Execute on the fund's behalf.
    /// @param data_ The data to be applied to the execution.
    /// @dev This function can only be used in `Executing`, `Pending` and `Liquidating` states.
    function execute(bytes calldata data_)
        public
        virtual
        when3States(State.Executing, State.Pending, State.Liquidating)
    {
        uint256 lastAmount = _beforeExecute();

        address action = comptroller.execAction();
        bytes memory response = vault.execute(action, data_);

        _afterExecute(response, lastAmount);

        emit Executed();
    }

    /// @notice The virtual function being called before execution.
    function _beforeExecute() internal virtual returns (uint256) {
        return 0;
    }

    /// @notice The virtual function being called after execution.
    function _afterExecute(bytes memory, uint256) internal virtual returns (uint256) {
        return 0;
    }
}

File 25 of 28 : ManagementFeeModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol";
import {FundProxyStorageUtils} from "../FundProxyStorageUtils.sol";
import {Errors} from "../utils/Errors.sol";

/// @title Management fee module
abstract contract ManagementFeeModule is FundProxyStorageUtils {
    using ABDKMath64x64 for int128;
    using ABDKMath64x64 for uint256;

    int128 private constant _FEE_BASE64x64 = 1 << 64;
    uint256 private constant _FEE_PERIOD = 31557600; // 365.25*24*60*60

    event ManagementFeeClaimed(address indexed manager, uint256 shareAmount);

    /// @notice Claim the accumulated management fee.
    /// @return The fee amount being claimed.
    function claimManagementFee() external virtual nonReentrant returns (uint256) {
        return _updateManagementFee();
    }

    /// @notice Initial the management fee claim time.
    function _initializeManagementFee() internal virtual {
        lastMFeeClaimTime = block.timestamp;
    }

    /// @notice Set the management fee in a yearly basis.
    /// @param feeRate_ The annual fee rate in a 1e4 base.
    /// @return The management fee rate.
    function _setManagementFeeRate(uint256 feeRate_) internal virtual returns (int128) {
        Errors._require(
            feeRate_ < _FUND_PERCENTAGE_BASE,
            Errors.Code.MANAGEMENT_FEE_MODULE_FEE_RATE_SHOULD_BE_LESS_THAN_FUND_BASE
        );
        return _setManagementFeeRate(feeRate_.divu(_FUND_PERCENTAGE_BASE));
    }

    /// @notice Set the management fee rate.
    /// @param feeRate64x64_ The annual fee rate in a 1e4 base.
    /// @return The management fee rate.
    /// @dev Calculate the effective fee rate to achieve the fee rate in an exponential model.
    function _setManagementFeeRate(int128 feeRate64x64_) internal returns (int128) {
        mFeeRate64x64 = uint256(1).fromUInt().sub(feeRate64x64_).ln().neg().div(_FEE_PERIOD.fromUInt()).exp();

        return mFeeRate64x64;
    }

    /// @notice Update the current management fee and mint to the manager right away.
    ///         Update the claim time as the basis of the accumulation afterward also.
    /// @return The share being minted this time.
    function _updateManagementFee() internal virtual returns (uint256) {
        uint256 currentTime = block.timestamp;
        uint256 totalShare = shareToken.grossTotalShare();

        uint256 shareDue = (mFeeRate64x64.pow(currentTime - lastMFeeClaimTime).sub(_FEE_BASE64x64)).mulu(totalShare);

        address manager = owner();
        shareToken.mint(manager, shareDue);
        lastMFeeClaimTime = currentTime;
        emit ManagementFeeClaimed(manager, shareDue);

        return shareDue;
    }
}

File 26 of 28 : PerformanceFeeModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol";
import {FundProxyStorageUtils} from "../FundProxyStorageUtils.sol";
import {LibFee} from "../libraries/LibFee.sol";
import {Errors} from "../utils/Errors.sol";

/// @title Performance fee module
abstract contract PerformanceFeeModule is FundProxyStorageUtils {
    using ABDKMath64x64 for int128;
    using ABDKMath64x64 for uint256;
    using SafeCast for int256;
    using SafeCast for uint256;

    int128 private constant _FEE_BASE64x64 = 1 << 64;
    uint256 private constant _FEE_PERIOD = 31557600; // 365.25*24*60*60
    address private constant _OUTSTANDING_ACCOUNT = address(1);

    event PerformanceFeeClaimed(address indexed manager, uint256 shareAmount);

    /// @notice Return the next crystallization time even if more than one period has passed.
    /// @return The available time for crystallization.
    function getNextCrystallizationTime() external view returns (uint256) {
        uint256 lastPeriod = _timeToPeriod(lastCrystallization);
        return _periodToTime(lastPeriod + 1);
    }

    /// @notice Check if the fund can be crystallized.
    /// @return The boolean of can crystallize or not.
    function isCrystallizable() public view virtual returns (bool) {
        uint256 nowPeriod = _timeToPeriod(block.timestamp);
        uint256 lastPeriod = _timeToPeriod(lastCrystallization);
        return nowPeriod > lastPeriod;
    }

    /// @notice Crystallize for the performance fee.
    /// @return Return the performance fee amount to be claimed.
    /// @dev This will check whether it can be crystallized or not.
    function crystallize() public virtual returns (uint256) {
        Errors._require(isCrystallizable(), Errors.Code.PERFORMANCE_FEE_MODULE_CAN_NOT_CRYSTALLIZED_YET);
        return _crystallize();
    }

    /// @notice Crystallize for the performance fee.
    /// @return Return the performance fee amount to be claimed.
    function _crystallize() internal virtual returns (uint256) {
        uint256 totalShare = shareToken.netTotalShare();
        if (totalShare == 0) return 0;
        uint256 grossAssetValue = __getGrossAssetValue();
        _updatePerformanceFee(grossAssetValue);
        address manager = owner();
        shareToken.move(_OUTSTANDING_ACCOUNT, manager, lastOutstandingShare);
        _updateGrossSharePrice(grossAssetValue);
        uint256 result = lastOutstandingShare;
        lastOutstandingShare = 0;
        pFeeSum = 0;
        lastCrystallization = block.timestamp;
        hwm64x64 = LibFee._max64x64(hwm64x64, lastGrossSharePrice64x64);
        emit PerformanceFeeClaimed(manager, result);

        return result;
    }

    /// @notice Convert the time to the number of crystallization periods.
    /// @param timestamp_ The timestamp to be converted.
    /// @return The period of crystallization.
    function _timeToPeriod(uint256 timestamp_) internal view returns (uint256) {
        Errors._require(timestamp_ >= crystallizationStart, Errors.Code.PERFORMANCE_FEE_MODULE_TIME_BEFORE_START);
        return (timestamp_ - crystallizationStart) / crystallizationPeriod;
    }

    /// @notice Convert the number of crystallization periods to time.
    /// @param period_ The period to be converted.
    /// @return The starting time of the period.
    function _periodToTime(uint256 period_) internal view returns (uint256) {
        return crystallizationStart + period_ * crystallizationPeriod;
    }

    /// @notice Get the gross asset value.
    /// @return The value of gross asset.
    function __getGrossAssetValue() internal view virtual returns (uint256);

    /// @notice Initialize the performance fee, crystallization time and high water mark.
    function _initializePerformanceFee() internal virtual {
        lastGrossSharePrice64x64 = _FEE_BASE64x64;
        hwm64x64 = _FEE_BASE64x64;
        crystallizationStart = block.timestamp;
        lastCrystallization = block.timestamp;
    }

    /// @notice Set the performance fee rate.
    /// @param feeRate_ The fee rate on a 1e4 basis.
    /// @return The performance fee rate.
    function _setPerformanceFeeRate(uint256 feeRate_) internal virtual returns (int128) {
        Errors._require(
            feeRate_ < _FUND_PERCENTAGE_BASE,
            Errors.Code.PERFORMANCE_FEE_MODULE_FEE_RATE_SHOULD_BE_LESS_THAN_BASE
        );
        pFeeRate64x64 = feeRate_.divu(_FUND_PERCENTAGE_BASE);
        return pFeeRate64x64;
    }

    /// @notice Set the crystallization period.
    /// @param period_ The crystallization period to be set in second.
    function _setCrystallizationPeriod(uint256 period_) internal virtual {
        Errors._require(period_ > 0, Errors.Code.PERFORMANCE_FEE_MODULE_CRYSTALLIZATION_PERIOD_TOO_SHORT);
        crystallizationPeriod = period_;
    }

    /// @notice Update the performance fee based on the performance since last
    ///         update. The fee will be minted as outstanding shares.
    /// @param grossAssetValue_ The gross asset value.
    function _updatePerformanceFee(uint256 grossAssetValue_) internal virtual {
        // Get accumulated wealth
        uint256 totalShare = shareToken.netTotalShare();
        if (totalShare == 0) {
            // net asset value should be 0 also
            return;
        }
        int128 grossSharePrice64x64 = grossAssetValue_.divu(totalShare);
        int256 wealth = LibFee
            ._max64x64(hwm64x64, grossSharePrice64x64)
            .sub(LibFee._max64x64(hwm64x64, lastGrossSharePrice64x64))
            .muli(totalShare.toInt256());
        int256 fee = pFeeRate64x64.muli(wealth);
        pFeeSum = LibFee._max(0, pFeeSum.toInt256() + fee).toUint256();
        uint256 netAssetValue = grossAssetValue_ - pFeeSum;
        uint256 outstandingShare = (totalShare * pFeeSum) / netAssetValue;
        if (outstandingShare > lastOutstandingShare) {
            shareToken.mint(_OUTSTANDING_ACCOUNT, outstandingShare - lastOutstandingShare);
        } else {
            shareToken.burn(_OUTSTANDING_ACCOUNT, lastOutstandingShare - outstandingShare);
        }
        lastOutstandingShare = outstandingShare;
        lastGrossSharePrice64x64 = grossSharePrice64x64;
    }

    /// @notice Update gross share price.
    /// @param grossAssetValue_ The gross asset value.
    function _updateGrossSharePrice(uint256 grossAssetValue_) internal virtual {
        uint256 totalShare = shareToken.netTotalShare();
        lastGrossSharePrice64x64 = grossAssetValue_.divu(totalShare);
    }
}

File 27 of 28 : ShareModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {FundProxyStorageUtils} from "../FundProxyStorageUtils.sol";
import {Errors} from "../utils/Errors.sol";

/// @title Share module
abstract contract ShareModule is FundProxyStorageUtils {
    using SafeERC20 for IERC20;

    uint256 private constant MINIMUM_SHARE = 1e3;

    event Purchased(address indexed user, uint256 assetAmount, uint256 shareAmount, uint256 bonusAmount);
    event Redeemed(address indexed user, uint256 assetAmount, uint256 shareAmount);
    event Pended(address indexed user, uint256 shareAmount, uint256 penaltyAmount);
    event PendingShareSettled();
    event RedemptionClaimed(address indexed user, uint256 assetAmount);

    /// @notice Calculate the share amount corresponding to the given balance.
    /// @param balance_ The balance of denomination.
    /// @return share The share amount.
    function calculateShare(uint256 balance_) external view returns (uint256 share) {
        uint256 grossAssetValue = __getGrossAssetValue();
        return _calculateShare(balance_, grossAssetValue);
    }

    function _calculateShare(uint256 balance_, uint256 grossAssetValue_) internal view virtual returns (uint256 share) {
        uint256 shareAmount = shareToken.grossTotalShare();
        if (shareAmount == 0) {
            // Handle initial purchase when balance_ > MINIMUM_SHARE, otherwise return share = 0
            if (balance_ > MINIMUM_SHARE) {
                share = balance_ - MINIMUM_SHARE;
            }
        } else {
            share = (shareAmount * balance_) / grossAssetValue_;
        }
    }

    /// @notice Calculate the balance amount corresponding to the given share amount.
    /// @param share_ The queried share amount.
    /// @return balance The balance.
    function calculateBalance(uint256 share_) external view returns (uint256 balance) {
        uint256 grossAssetValue = __getGrossAssetValue();
        balance = _calculateBalance(share_, grossAssetValue);
    }

    function _calculateBalance(uint256 share_, uint256 grossAssetValue_)
        internal
        view
        virtual
        returns (uint256 balance)
    {
        uint256 shareAmount = shareToken.grossTotalShare();
        Errors._require(share_ <= shareAmount, Errors.Code.SHARE_MODULE_SHARE_AMOUNT_TOO_LARGE);
        if (shareAmount == 0) {
            balance = 0;
        } else {
            balance = (share_ * grossAssetValue_) / shareAmount;
        }
    }

    /// @notice determine pending statue could be resolvable or not
    /// @param applyPenalty_ true if enable penalty otherwise false
    /// @return true if resolvable otherwise false
    function isPendingResolvable(bool applyPenalty_) external view returns (bool) {
        uint256 grossAssetValue = __getGrossAssetValue();

        return _isPendingResolvable(applyPenalty_, grossAssetValue);
    }

    function _isPendingResolvable(bool applyPenalty_, uint256 grossAssetValue_) internal view returns (bool) {
        uint256 redeemShare = _getResolvePendingShare(applyPenalty_);
        uint256 redeemShareBalance = _calculateBalance(redeemShare, grossAssetValue_);
        uint256 reserve = getReserve();

        return reserve >= redeemShareBalance;
    }

    /// @notice Calculate the max redeemable balance of the given share amount.
    /// @param share_ The share amount to be queried.
    /// @return shareLeft The share amount left due to insufficient reserve.
    /// @return balance The max redeemable balance from reserve.
    function calculateRedeemableBalance(uint256 share_) external view returns (uint256 shareLeft, uint256 balance) {
        uint256 grossAssetValue = __getGrossAssetValue();
        return _calculateRedeemableBalance(share_, grossAssetValue);
    }

    function _calculateRedeemableBalance(uint256 share_, uint256 grossAssetValue_)
        internal
        view
        virtual
        returns (uint256 shareLeft, uint256 balance)
    {
        balance = _calculateBalance(share_, grossAssetValue_);
        uint256 reserve = getReserve();

        // insufficient reserve
        if (balance > reserve) {
            uint256 shareToBurn = _calculateShare(reserve, grossAssetValue_);
            shareLeft = share_ - shareToBurn;
            balance = reserve;
        }
    }

    /// @notice Purchase share with the given balance.
    /// @return share The purchased share amount.
    /// @dev This function can only be used in `Executing` and `Pending` states.

    function purchase(uint256 balance_)
        external
        virtual
        whenStates(State.Executing, State.Pending)
        nonReentrant
        returns (uint256 share)
    {
        // Purchase balance need to greater than zero
        Errors._require(balance_ > 0, Errors.Code.SHARE_MODULE_PURCHASE_ZERO_BALANCE);

        share = _purchase(msg.sender, balance_);
    }

    function _purchase(address user_, uint256 balance_) internal virtual returns (uint256 share) {
        uint256 grossAssetValue = _beforePurchase();
        share = _addShare(user_, balance_, grossAssetValue);

        // Purchase share need to greater than zero
        Errors._require(share > 0, Errors.Code.SHARE_MODULE_PURCHASE_ZERO_SHARE);

        // No bonus to prevent frontrun if the purchase is simultaneous with pending state transition
        uint256 bonus;
        if (state == State.Pending && pendingStartTime != block.timestamp) {
            uint256 penalty = _getPendingPenalty();
            bonus = (share * (penalty)) / (_FUND_PERCENTAGE_BASE - penalty);
            bonus = currentTotalPendingBonus > bonus ? bonus : currentTotalPendingBonus;
            currentTotalPendingBonus -= bonus;
            shareToken.move(address(this), user_, bonus);
            share += bonus;
        }
        grossAssetValue += balance_;
        denomination.safeTransferFrom(user_, address(vault), balance_);
        _afterPurchase(grossAssetValue);

        emit Purchased(user_, balance_, share, bonus);
    }

    /// @notice Redeem with the given share amount. Need to wait when fund is under liquidation
    /// @dev This function can only be used in `Executing`, `Pending` and `Closed` states.
    function redeem(uint256 share_, bool acceptPending_)
        external
        virtual
        when3States(State.Executing, State.Pending, State.Closed)
        nonReentrant
        returns (uint256 balance)
    {
        // Redeem share need to greater than zero
        Errors._require(share_ > 0, Errors.Code.SHARE_MODULE_REDEEM_ZERO_SHARE);

        // Check redeem share need to greater than user share they own
        uint256 userShare = shareToken.balanceOf(msg.sender);
        Errors._require(share_ <= userShare, Errors.Code.SHARE_MODULE_INSUFFICIENT_SHARE);

        // Claim pending redemption if need
        if (isPendingRedemptionClaimable(msg.sender)) {
            _claimPendingRedemption(msg.sender);
        }

        // Execute redeem operation
        if (state == State.Pending) {
            balance = _redeemPending(msg.sender, share_, acceptPending_);
        } else {
            balance = _redeem(msg.sender, share_, acceptPending_);
        }
    }

    function _redeem(
        address user_,
        uint256 share_,
        bool acceptPending_
    ) internal virtual returns (uint256) {
        uint256 grossAssetValue = _beforeRedeem();
        (uint256 shareLeft, uint256 balance) = _calculateRedeemableBalance(share_, grossAssetValue);

        uint256 shareRedeemed = share_ - shareLeft;
        shareToken.burn(user_, shareRedeemed);

        if (shareLeft != 0) {
            _pend();
            _redeemPending(user_, shareLeft, acceptPending_);
        }
        grossAssetValue -= balance;
        denomination.safeTransferFrom(address(vault), user_, balance);
        _afterRedeem(grossAssetValue);
        emit Redeemed(user_, balance, shareRedeemed);

        return balance;
    }

    function _redeemPending(
        address user_,
        uint256 share_,
        bool acceptPending_
    ) internal virtual returns (uint256) {
        Errors._require(acceptPending_, Errors.Code.SHARE_MODULE_REDEEM_IN_PENDING_WITHOUT_PERMISSION);

        // Add the current pending round to pending user info for the first redeem
        if (pendingUsers[user_].pendingShare == 0) {
            pendingUsers[user_].pendingRound = currentPendingRound();
        } else {
            // Confirm user pending share is in the current pending round
            Errors._require(
                pendingUsers[user_].pendingRound == currentPendingRound(),
                Errors.Code.SHARE_MODULE_PENDING_ROUND_INCONSISTENT
            );
        }

        // Calculate and update pending information
        uint256 penalty = _getPendingPenalty();
        uint256 effectiveShare = (share_ * (_FUND_PERCENTAGE_BASE - penalty)) / _FUND_PERCENTAGE_BASE;
        uint256 penaltyShare = share_ - effectiveShare;
        pendingUsers[user_].pendingShare += effectiveShare;
        currentTotalPendingShare += effectiveShare;
        currentTotalPendingBonus += penaltyShare;
        shareToken.move(user_, address(this), share_);
        emit Pended(user_, effectiveShare, penaltyShare);

        return 0;
    }

    /// @notice Claim the settled pending redemption.
    /// @param user_ address want to be claim
    /// @return balance The balance being claimed.
    function claimPendingRedemption(address user_) external nonReentrant returns (uint256 balance) {
        Errors._require(isPendingRedemptionClaimable(user_), Errors.Code.SHARE_MODULE_PENDING_REDEMPTION_NOT_CLAIMABLE);
        balance = _claimPendingRedemption(user_);
    }

    function _claimPendingRedemption(address user_) internal returns (uint256 balance) {
        balance = _calcPendingRedemption(user_);

        // reset pending user to zero value
        delete pendingUsers[user_];

        if (balance > 0) {
            denomination.safeTransfer(user_, balance);
        }
        emit RedemptionClaimed(user_, balance);
    }

    /// @notice the length of pendingRoundList, means current pending round
    /// @return current pending round
    function currentPendingRound() public view returns (uint256) {
        return pendingRoundList.length;
    }

    /// @notice Determine user could claim pending redemption or not
    /// @param user_ address could be claimable
    /// @return true if claimable otherwise false
    function isPendingRedemptionClaimable(address user_) public view returns (bool) {
        return pendingUsers[user_].pendingRound < currentPendingRound() && pendingUsers[user_].pendingShare > 0;
    }

    function _calcPendingRedemption(address user_) internal view returns (uint256) {
        PendingUserInfo storage pendingUser = pendingUsers[user_];
        PendingRoundInfo storage pendingRoundInfo = pendingRoundList[pendingUser.pendingRound];
        return (pendingRoundInfo.totalRedemption * pendingUser.pendingShare) / pendingRoundInfo.totalPendingShare;
    }

    function _getResolvePendingShare(bool applyPenalty_) internal view returns (uint256) {
        if (applyPenalty_) {
            return currentTotalPendingShare;
        } else {
            return currentTotalPendingShare + currentTotalPendingBonus;
        }
    }

    function _getPendingPenalty() internal view virtual returns (uint256) {
        return comptroller.pendingPenalty();
    }

    function getReserve() public view virtual returns (uint256);

    function __getGrossAssetValue() internal view virtual returns (uint256);

    function _beforePurchase() internal virtual returns (uint256) {
        return 0;
    }

    function _afterPurchase(uint256 grossAssetValue_) internal virtual {
        grossAssetValue_;
        return;
    }

    function _beforeRedeem() internal virtual returns (uint256) {
        return 0;
    }

    function _afterRedeem(uint256 grossAssetValue_) internal virtual {
        grossAssetValue_;
        return;
    }

    function _addShare(
        address user_,
        uint256 balance_,
        uint256 grossAssetValue_
    ) internal virtual returns (uint256 share) {
        share = _calculateShare(balance_, grossAssetValue_);

        // Lock MINIMUM_SHARE to share token contract itself for the initial puchase
        // Share token contract has blocked transfer from itself
        if (shareToken.grossTotalShare() == 0) {
            shareToken.mint(address(shareToken), MINIMUM_SHARE);
        }

        shareToken.mint(user_, share);
    }

    function _settlePendingShare(bool applyPenalty_) internal returns (uint256 totalRedemption) {
        // Get total share for the settle
        uint256 redeemShare = _getResolvePendingShare(applyPenalty_);
        if (redeemShare == 0) return 0;

        // Calculate the total redemption depending on the redeemShare
        totalRedemption = _redeem(address(this), redeemShare, false);

        // Settle this round and store settle info to round list
        pendingRoundList.push(
            PendingRoundInfo({totalPendingShare: currentTotalPendingShare, totalRedemption: totalRedemption})
        );

        currentTotalPendingShare = 0; // reset currentTotalPendingShare
        if (applyPenalty_) {
            // if applyPenalty is true that means there are some share as bonus share
            // need to burn these bonus share if they are remaining
            if (currentTotalPendingBonus != 0) {
                shareToken.burn(address(this), currentTotalPendingBonus); // burn unused bonus
                currentTotalPendingBonus = 0;
            }
        } else {
            currentTotalPendingBonus = 0;
        }

        emit PendingShareSettled();
    }
}

File 28 of 28 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library Errors {
    error RevertCode(Code errorCode);

    enum Code {
        COMPTROLLER_HALTED, // 0: "Halted"
        COMPTROLLER_BANNED, // 1: "Banned"
        COMPTROLLER_ZERO_ADDRESS, // 2: "Zero address"
        COMPTROLLER_TOS_AND_SIGS_LENGTH_INCONSISTENT, // 3: "tos and sigs length are inconsistent"
        COMPTROLLER_BEACON_IS_INITIALIZED, // 4: "Beacon is initialized"
        COMPTROLLER_DENOMINATIONS_AND_DUSTS_LENGTH_INCONSISTENT, // 5: "denominations and dusts length are inconsistent"
        IMPLEMENTATION_ASSET_LIST_NOT_EMPTY, // 6: "assetList is not empty"
        IMPLEMENTATION_INVALID_DENOMINATION, // 7: "Invalid denomination"
        IMPLEMENTATION_INVALID_MORTGAGE_TIER, // 8: "Mortgage tier not set in comptroller"
        IMPLEMENTATION_PENDING_SHARE_NOT_RESOLVABLE, // 9: "pending share is not resolvable"
        IMPLEMENTATION_PENDING_NOT_START, // 10: "Pending does not start"
        IMPLEMENTATION_PENDING_NOT_EXPIRE, // 11: "Pending does not expire"
        IMPLEMENTATION_INVALID_ASSET, // 12: "Invalid asset"
        IMPLEMENTATION_INSUFFICIENT_TOTAL_VALUE_FOR_EXECUTION, // 13: "Insufficient total value for execution"
        FUND_PROXY_FACTORY_INVALID_CREATOR, // 14: "Invalid creator"
        FUND_PROXY_FACTORY_INVALID_DENOMINATION, // 15: "Invalid denomination"
        FUND_PROXY_FACTORY_INVALID_MORTGAGE_TIER, // 16: "Mortgage tier not set in comptroller"
        FUND_PROXY_STORAGE_UTILS_INVALID_DENOMINATION, // 17: "Invalid denomination"
        FUND_PROXY_STORAGE_UTILS_UNKNOWN_OWNER, // 18: "Unknown owner"
        FUND_PROXY_STORAGE_UTILS_WRONG_ALLOWANCE, // 19: "Wrong allowance"
        FUND_PROXY_STORAGE_UTILS_IS_NOT_ZERO, // 20: "Is not zero value or address "
        FUND_PROXY_STORAGE_UTILS_IS_ZERO, // 21: "Is zero value or address"
        MORTGAGE_VAULT_FUND_MORTGAGED, // 22: "Fund mortgaged"
        SHARE_TOKEN_INVALID_FROM, // 23: "Invalid from"
        SHARE_TOKEN_INVALID_TO, // 24: "Invalid to"
        TASK_EXECUTOR_TOS_AND_DATAS_LENGTH_INCONSISTENT, // 25: "tos and datas length inconsistent"
        TASK_EXECUTOR_TOS_AND_CONFIGS_LENGTH_INCONSISTENT, // 26: "tos and configs length inconsistent"
        TASK_EXECUTOR_INVALID_COMPTROLLER_DELEGATE_CALL, // 27: "Invalid comptroller delegate call"
        TASK_EXECUTOR_INVALID_COMPTROLLER_CONTRACT_CALL, // 28: "Invalid comptroller contract call"
        TASK_EXECUTOR_INVALID_DEALING_ASSET, // 29: "Invalid dealing asset"
        TASK_EXECUTOR_REFERENCE_TO_OUT_OF_LOCALSTACK, // 30: "Reference to out of localStack"
        TASK_EXECUTOR_RETURN_NUM_AND_PARSED_RETURN_NUM_NOT_MATCHED, // 31: "Return num and parsed return num not matched"
        TASK_EXECUTOR_ILLEGAL_LENGTH_FOR_PARSE, // 32: "Illegal length for _parse"
        TASK_EXECUTOR_STACK_OVERFLOW, // 33: "Stack overflow"
        TASK_EXECUTOR_INVALID_INITIAL_ASSET, // 34: "Invalid initial asset"
        TASK_EXECUTOR_NON_ZERO_QUOTA, // 35: "Quota is not zero"
        AFURUCOMBO_DUPLICATED_TOKENSOUT, // 36: "Duplicated tokensOut"
        AFURUCOMBO_REMAINING_TOKENS, // 37: "Furucombo has remaining tokens"
        AFURUCOMBO_TOKENS_AND_AMOUNTS_LENGTH_INCONSISTENT, // 38: "Token length != amounts length"
        AFURUCOMBO_INVALID_COMPTROLLER_HANDLER_CALL, // 39: "Invalid comptroller handler call"
        CHAINLINK_ASSETS_AND_AGGREGATORS_INCONSISTENT, // 40: "assets.length == aggregators.length"
        CHAINLINK_ZERO_ADDRESS, // 41: "Zero address"
        CHAINLINK_EXISTING_ASSET, // 42: "Existing asset"
        CHAINLINK_NON_EXISTENT_ASSET, // 43: "Non-existent asset"
        CHAINLINK_INVALID_PRICE, // 44: "Invalid price"
        CHAINLINK_STALE_PRICE, // 45: "Stale price"
        ASSET_REGISTRY_UNREGISTERED, // 46: "Unregistered"
        ASSET_REGISTRY_BANNED_RESOLVER, // 47: "Resolver has been banned"
        ASSET_REGISTRY_ZERO_RESOLVER_ADDRESS, // 48: "Resolver zero address"
        ASSET_REGISTRY_ZERO_ASSET_ADDRESS, // 49: "Asset zero address"
        ASSET_REGISTRY_REGISTERED_RESOLVER, // 50: "Resolver is registered"
        ASSET_REGISTRY_NON_REGISTERED_RESOLVER, // 51: "Asset not registered"
        ASSET_REGISTRY_NON_BANNED_RESOLVER, // 52: "Resolver is not banned"
        ASSET_ROUTER_ASSETS_AND_AMOUNTS_LENGTH_INCONSISTENT, // 53: "assets length != amounts length"
        ASSET_ROUTER_NEGATIVE_VALUE, // 54: "Negative value"
        RESOLVER_ASSET_VALUE_NEGATIVE, // 55: "Resolver's asset value < 0"
        RESOLVER_ASSET_VALUE_POSITIVE, // 56: "Resolver's asset value > 0"
        RCURVE_STABLE_ZERO_ASSET_ADDRESS, // 57: "Zero asset address"
        RCURVE_STABLE_ZERO_POOL_ADDRESS, // 58: "Zero pool address"
        RCURVE_STABLE_ZERO_VALUED_ASSET_ADDRESS, // 59: "Zero valued asset address"
        RCURVE_STABLE_VALUED_ASSET_DECIMAL_NOT_MATCH_VALUED_ASSET, // 60: "Valued asset decimal not match valued asset"
        RCURVE_STABLE_POOL_INFO_IS_NOT_SET, // 61: "Pool info is not set"
        ASSET_MODULE_DIFFERENT_ASSET_REMAINING, // 62: "Different asset remaining"
        ASSET_MODULE_FULL_ASSET_CAPACITY, // 63: "Full Asset Capacity"
        MANAGEMENT_FEE_MODULE_FEE_RATE_SHOULD_BE_LESS_THAN_FUND_BASE, // 64: "Fee rate should be less than 100%"
        PERFORMANCE_FEE_MODULE_CAN_NOT_CRYSTALLIZED_YET, // 65: "Can not crystallized yet"
        PERFORMANCE_FEE_MODULE_TIME_BEFORE_START, // 66: "Time before start"
        PERFORMANCE_FEE_MODULE_FEE_RATE_SHOULD_BE_LESS_THAN_BASE, // 67: "Fee rate should be less than 100%"
        PERFORMANCE_FEE_MODULE_CRYSTALLIZATION_PERIOD_TOO_SHORT, // 68: "Crystallization period too short"
        SHARE_MODULE_SHARE_AMOUNT_TOO_LARGE, // 69: "The requesting share amount is greater than total share amount"
        SHARE_MODULE_PURCHASE_ZERO_BALANCE, // 70: "The purchased balance is zero"
        SHARE_MODULE_PURCHASE_ZERO_SHARE, // 71: "The share purchased need to greater than zero"
        SHARE_MODULE_REDEEM_ZERO_SHARE, // 72: "The redeem share is zero"
        SHARE_MODULE_INSUFFICIENT_SHARE, // 73: "Insufficient share amount"
        SHARE_MODULE_REDEEM_IN_PENDING_WITHOUT_PERMISSION, // 74: "Redeem in pending without permission"
        SHARE_MODULE_PENDING_ROUND_INCONSISTENT, // 75: "user pending round and current pending round are inconsistent"
        SHARE_MODULE_PENDING_REDEMPTION_NOT_CLAIMABLE // 76: "Pending redemption is not claimable"
    }

    function _require(bool condition_, Code errorCode_) internal pure {
        if (!condition_) revert RevertCode(errorCode_);
    }

    function _revertMsg(string memory functionName_, string memory reason_) internal pure {
        revert(string(abi.encodePacked(functionName_, ": ", reason_)));
    }

    function _revertMsg(string memory functionName_) internal pure {
        _revertMsg(functionName_, "Unspecified");
    }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"enum FundProxyStorageV1.State","name":"current","type":"uint8"}],"name":"InvalidState","type":"error"},{"inputs":[{"internalType":"enum Errors.Code","name":"errorCode","type":"uint8"}],"name":"RevertCode","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"AssetAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"AssetRemoved","type":"event"},{"anonymous":false,"inputs":[],"name":"Executed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"manager","type":"address"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"ManagementFeeClaimed","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":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penaltyAmount","type":"uint256"}],"name":"Pended","type":"event"},{"anonymous":false,"inputs":[],"name":"PendingShareSettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"manager","type":"address"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"PerformanceFeeClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bonusAmount","type":"uint256"}],"name":"Purchased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"Redeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"RedemptionClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum FundProxyStorageV1.State","name":"to","type":"uint8"}],"name":"StateTransited","type":"event"},{"inputs":[{"internalType":"address","name":"asset_","type":"address"}],"name":"addAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"share_","type":"uint256"}],"name":"calculateBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"share_","type":"uint256"}],"name":"calculateRedeemableBalance","outputs":[{"internalType":"uint256","name":"shareLeft","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"balance_","type":"uint256"}],"name":"calculateShare","outputs":[{"internalType":"uint256","name":"share","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimManagementFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"}],"name":"claimPendingRedemption","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"close","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract IComptroller","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"crystallizationPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"crystallizationStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"crystallize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentPendingRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTotalPendingBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTotalPendingShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"denomination","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finalize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAssetList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset_","type":"address"}],"name":"getAssetValue","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGrossAssetValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextCrystallizationTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hwm64x64","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"level_","type":"uint256"},{"internalType":"contract IComptroller","name":"comptroller_","type":"address"},{"internalType":"contract IERC20","name":"denomination_","type":"address"},{"internalType":"contract IShareToken","name":"shareToken_","type":"address"},{"internalType":"uint256","name":"mFeeRate_","type":"uint256"},{"internalType":"uint256","name":"pFeeRate_","type":"uint256"},{"internalType":"uint256","name":"crystallizationPeriod_","type":"uint256"},{"internalType":"address","name":"newOwner_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isCrystallizable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"}],"name":"isPendingRedemptionClaimable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"applyPenalty_","type":"bool"}],"name":"isPendingResolvable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastCrystallization","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastGrossSharePrice64x64","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastMFeeClaimTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastOutstandingShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"level","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mFeeRate64x64","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mortgageVault","outputs":[{"internalType":"contract IMortgageVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pFeeRate64x64","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pFeeSum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pendingRoundList","outputs":[{"internalType":"uint256","name":"totalPendingShare","type":"uint256"},{"internalType":"uint256","name":"totalRedemption","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pendingUsers","outputs":[{"internalType":"uint256","name":"pendingRound","type":"uint256"},{"internalType":"uint256","name":"pendingShare","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"balance_","type":"uint256"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"share","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"share_","type":"uint256"},{"internalType":"bool","name":"acceptPending_","type":"bool"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset_","type":"address"}],"name":"removeAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resume","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"crystallizationPeriod_","type":"uint256"}],"name":"setCrystallizationPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mFeeRate_","type":"uint256"}],"name":"setManagementFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pFeeRate_","type":"uint256"}],"name":"setPerformanceFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shareToken","outputs":[{"internalType":"contract IShareToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"state","outputs":[{"internalType":"enum FundProxyStorageV1.State","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IDSProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b506200001d3362000031565b600180556200002b62000081565b620000ee565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000546001600160a01b03163314620000e05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640160405180910390fd5b620000ec600062000031565b565b615ba480620000fe6000396000f3fe608060405234801561001057600080fd5b50600436106103265760003560e01c80636c9fa59e116101b857806393e5e90f11610104578063cae1ab97116100a2578063e5c7cf9f1161007c578063e5c7cf9f1461066c578063efef39a114610674578063f2fde38b14610687578063fbfa77cf1461069a57600080fd5b8063cae1ab9714610633578063cf04327a14610646578063d65a06b01461065957600080fd5b8063b44f9b48116100de578063b44f9b48146105ff578063b53fdc0814610607578063bb5dcb7614610610578063c19d93fb1461061957600080fd5b806393e5e90f146105b257806395459b58146105c55780639f040df5146105ec57600080fd5b8063758df43a11610171578063869603c01161014b578063869603c01461057257806387b9bb901461057b5780638bca6d161461058e5780638da5cb5b146105a157600080fd5b8063758df43a1461054e57806375a80cf0146105565780638570f7bf1461055f57600080fd5b80636c9fa59e146105045780636cf50c46146105175780636fd5ae151461052b57806370f0a32e14610534578063715018a61461053d578063731f24051461054557600080fd5b80634126c8921161027757806359bf5d39116102305780635fe3b5671161020a5780635fe3b567146104bd57806364b733da146104d55780636a3370d7146104de5780636bc50bc6146104f157600080fd5b806359bf5d39146104995780635b22130d146104a15780635d59f8b1146104b557600080fd5b80634126c8921461045357806343d726d61461045b5780634a5e42b1146104635780634bb278f314610476578063508c9dbf1461047e57806350fc2f431461049157600080fd5b80632507d575116102e45780632ae60b17116102be5780632ae60b17146103e65780633765c0b8146103f35780633c0b2771146104085780633ed7d3d21461043057600080fd5b80632507d575146103ab57806328a07025146103cb578063298410e5146103d357600080fd5b80628593821461032b578063046f7da21461034757806309c5eabe14610351578063127f3b61146103645780631352436e14610377578063137d524214610380575b600080fd5b610334600c5481565b6040519081526020015b60405180910390f35b61034f6106ad565b005b61034f61035f366004615406565b610725565b61034f610372366004615478565b610781565b61033460145481565b600854610393906001600160a01b031681565b6040516001600160a01b03909116815260200161033e565b6011546103b890600f0b81565b604051600f9190910b815260200161033e565b61034f6107c4565b61034f6103e13660046154a6565b610923565b6012546103b890600f0b81565b6103fb61098d565b60405161033e9190615507565b61041b610416366004615478565b61099e565b6040805192835260208301919091520161033e565b61044361043e3660046154a6565b6109c1565b604051901515815260200161033e565b610334610a12565b61034f610a3b565b61034f6104713660046154a6565b610b3c565b61034f610b97565b61033461048c366004615478565b610eeb565b600e54610334565b610334610f09565b6012546103b890600160801b9004600f0b81565b610334610f7e565b6004546103939061010090046001600160a01b031681565b61033460165481565b61034f6104ec36600461551a565b610ff3565b61041b6104ff366004615478565b6110ce565b600654610393906001600160a01b031681565b6011546103b890600160801b9004600f0b81565b61033460025481565b61033460035481565b61034f6110fc565b610334600d5481565b610334611132565b61033460135481565b61033461056d3660046154a6565b6112d3565b61033460155481565b6104436105893660046155ad565b611323565b600554610393906001600160a01b031681565b6000546001600160a01b0316610393565b6103346105c03660046154a6565b61133a565b61041b6105d33660046154a6565b600f602052600090815260409020805460019091015482565b61034f6105fa366004615478565b611441565b610334611483565b61033460175481565b61033460105481565b6004546106269060ff1681565b60405161033e91906155e0565b610334610641366004615478565b6114be565b61034f610654366004615478565b6114d5565b6103346106673660046155fa565b611513565b61044361163f565b610334610682366004615478565b611663565b61034f6106953660046154a6565b6116be565b600754610393906001600160a01b031681565b600260015414156106d95760405162461bcd60e51b81526004016106d09061562a565b60405180910390fd5b600260015560036106e981611759565b60006106f3611132565b905061070a6107036001836117a8565b60096117dc565b61071460016117fc565b5061071d611959565b505060018055565b600260015414156107485760405162461bcd60e51b81526004016106d09061562a565b60026001556000546001600160a01b031633146107775760405162461bcd60e51b81526004016106d090615661565b61071d8282611973565b6000546001600160a01b031633146107ab5760405162461bcd60e51b81526004016106d090615661565b60016107b681611759565b6107bf82611aca565b505050565b600260015414156107e75760405162461bcd60e51b81526004016106d09061562a565b60026001556003546107fc901515600a6117dc565b61088d600460019054906101000a90046001600160a01b03166001600160a01b0316638ec128d26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610852573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108769190615696565b60035461088391906156c5565b421015600b6117dc565b610895611b10565b5061089e611b23565b61091d600460019054906101000a90046001600160a01b03166001600160a01b03166396d19c7f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061091891906156dd565b611b3d565b60018055565b600260015414156109465760405162461bcd60e51b81526004016106d09061562a565b60026001556000546001600160a01b031633146109755760405162461bcd60e51b81526004016106d090615661565b61097e81611b8d565b610986611c70565b5060018055565b60606109996009611cfc565b905090565b60008060006109ab611dd4565b90506109b78482611dde565b9250925050915091565b60006109cc600e5490565b6001600160a01b0383166000908152600f6020526040902054108015610a0c57506001600160a01b0382166000908152600f602052604090206001015415155b92915050565b600080610a20601754611e28565b9050610a35610a308260016156c5565b611e54565b91505090565b6000546001600160a01b03163314610a655760405162461bcd60e51b81526004016106d090615661565b60026001541415610a885760405162461bcd60e51b81526004016106d09061562a565b600260018190556004610a9b8282611e71565b610aa560006117fc565b50600260045460ff166005811115610abf57610abf6155ca565b1415610acf57610acd611b10565b505b610ad7611eeb565b600854604051630f41a04d60e11b81523360048201526001600160a01b0390911690631e83409a90602401600060405180830381600087803b158015610b1c57600080fd5b505af1158015610b30573d6000803e3d6000fd5b50506001805550505050565b60026001541415610b5f5760405162461bcd60e51b81526004016106d09061562a565b60026001556000546001600160a01b03163314610b8e5760405162461bcd60e51b81526004016106d090615661565b61098681611f4d565b60026001541415610bba5760405162461bcd60e51b81526004016106d09061562a565b60026001556000546001600160a01b03163314610be95760405162461bcd60e51b81526004016106d090615661565b610bf1611f9c565b610c05610bfc61098d565b511560066117dc565b60048054600554604051635bb9b1d160e01b81526001600160a01b0391821693810193909352610c88926101009092041690635bb9b1d190602401602060405180830381865afa158015610c5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c8191906156fa565b60076117dc565b600554610c9d906001600160a01b0316611b8d565b610d1c600460019054906101000a90046001600160a01b03166001600160a01b031663a24d7eda6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cf3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1791906156dd565b611fb1565b610d2542601055565b60128054600160401b6001600160801b0319909116179055601180546001600160801b0316600160c01b17905542601581905560175560048054600254604051630c7cc1e960e21b815292830152600091829161010090046001600160a01b0316906331f307a4906024016040805180830381865afa158015610dac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd09190615717565b91509150610ddf8260086117dc565b801561071d57600854604080516323b1648360e21b815290516000926001600160a01b031691638ec5920c9160048083019260209291908290030181865afa158015610e2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5391906156dd565b9050610e6a6001600160a01b038216333085612114565b600854610e84906001600160a01b0383811691168461216c565b600854604051630df18baf60e11b8152600481018490526001600160a01b0390911690631be3175e90602401600060405180830381600087803b158015610eca57600080fd5b505af1158015610ede573d6000803e3d6000fd5b5050505050505060018055565b600080610ef6611dd4565b9050610f028382612281565b9392505050565b6005546007546040516370a0823160e01b81526001600160a01b03918216600482015260009291909116906370a0823190602401602060405180830381865afa158015610f5a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109999190615696565b600060026001541415610fa35760405162461bcd60e51b81526004016106d09061562a565b60026001556000546001600160a01b03163314610fd25760405162461bcd60e51b81526004016106d090615661565b60026003610fe08282611e71565b610fe8612338565b925050506001805590565b6000610ffe81611759565b61100789612354565b6110108861236d565b611019876123b8565b61102286612471565b61102b856124b1565b5061103584611aca565b5061103f836124d6565b6110a9886001600160a01b031663087936656040518163ffffffff1660e01b8152600401602060405180830381865afa158015611080573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a491906156dd565b6124e9565b6110b2886126b0565b6110bb82611b3d565b6110c361274a565b505050505050505050565b600e81815481106110de57600080fd5b60009182526020909120600290910201805460019091015490915082565b6000546001600160a01b031633146111265760405162461bcd60e51b81526004016106d090615661565b6111306000611b3d565b565b60008061113d61098d565b805190915060008167ffffffffffffffff81111561115d5761115d615745565b604051908082528060200260200182016040528015611186578160200160208202803683370190505b50905060005b8281101561124d578381815181106111a6576111a661575b565b60209081029190910101516007546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a0823190602401602060405180830381865afa1580156111fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121e9190615696565b8282815181106112305761123061575b565b60209081029190910101528061124581615771565b91505061118c565b5061125661275f565b600554604051630a42d0b160e11b81526001600160a01b0392831692631485a1629261128a9288928792169060040161578c565b602060405180830381865afa1580156112a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112cb9190615696565b935050505090565b6000600260015414156112f85760405162461bcd60e51b81526004016106d09061562a565b6002600155611310611309836109c1565b604c6117dc565b611319826127d8565b6001805592915050565b60008061132e611dd4565b9050610f0283826117a8565b6007546040516370a0823160e01b81526001600160a01b0391821660048201526000918291908416906370a0823190602401602060405180830381865afa158015611389573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ad9190615696565b9050806113bd5750600092915050565b6113c561275f565b60055460405163dedcf67560e01b81526001600160a01b03868116600483015260248201859052918216604482015291169063dedcf67590606401602060405180830381865afa15801561141d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f029190615696565b6000546001600160a01b0316331461146b5760405162461bcd60e51b81526004016106d090615661565b600161147681611759565b61147f826124d6565b5050565b6000600260015414156114a85760405162461bcd60e51b81526004016106d09061562a565b60026001556114b5612869565b90506001805590565b6000806114c9611dd4565b9050610f0283826128bc565b6000546001600160a01b031633146114ff5760405162461bcd60e51b81526004016106d090615661565b600161150a81611759565b6107bf826124b1565b6000600260036005611526838383612966565b600260015414156115495760405162461bcd60e51b81526004016106d09061562a565b600260015561155b86151560486117dc565b6006546040516370a0823160e01b81523360048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa1580156115a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115c89190615696565b90506115d88188111560496117dc565b6115e1336109c1565b156115f1576115ef336127d8565b505b600360045460ff16600581111561160a5761160a6155ca565b14156116225761161b338888612a0e565b9450611630565b61162d338888612be4565b94505b50506001805550909392505050565b60008061164b42611e28565b9050600061165a601754611e28565b90911192915050565b6000600260036116738282611e71565b600260015414156116965760405162461bcd60e51b81526004016106d09061562a565b60026001556116a884151560466117dc565b6116b23385612d17565b60018055949350505050565b6000546001600160a01b031633146116e85760405162461bcd60e51b81526004016106d090615661565b6001600160a01b03811661174d5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016106d0565b61175681611b3d565b50565b80600581111561176b5761176b6155ca565b60045460ff166005811115611782576117826155ca565b14611756576004805460405163683f44bb60e11b81526106d09260ff90921691016155e0565b6000806117b484612ec7565b905060006117c28285612281565b905060006117ce610f09565b919091101595945050505050565b8161147f578060405163193106d160e21b81526004016106d091906157f8565b60008061180883612ec7565b9050806118185750600092915050565b61182430826000612be4565b60408051808201909152600c8054825260208201838152600e8054600181018255600091825293517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd60029095029485015590517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fe90930192909255559150821561192457600d541561191f57600654600d54604051632770a7eb60e21b815230600482015260248101919091526001600160a01b0390911690639dc29fac90604401600060405180830381600087803b15801561190157600080fd5b505af1158015611915573d6000803e3d6000fd5b50506000600d5550505b61192a565b6000600d555b6040517f2f7beeb5ae17819093f004bb67cc96d1d7617a1518b537c889ff41189303b9bd90600090a150919050565b600361196481611759565b60006003556117566002612ee7565b600260036004611984838383612966565b600061198e611dd4565b90506000600460019054906101000a90046001600160a01b03166001600160a01b031663bf11a1146040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a0991906156dd565b600754604051631cff79cd60e01b81529192506000916001600160a01b0390911690631cff79cd90611a439085908c908c9060040161580c565b6000604051808303816000875af1158015611a62573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a8a91908101906158a9565b9050611a968184612f44565b506040517f68f46c45a243a0e9065a97649faf9a5afe1692f2679e650c2f853b9cd734cc0e90600090a15050505050505050565b6000611adb612710831060436117dc565b611ae78261271061306d565b601280546001600160801b03908116600160801b939091168302179081905504600f0b92915050565b6000611b1a612869565b506109996130a4565b6003611b2e81611759565b60006003556117566004612ee7565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60048054600254604051637569594b60e11b8152928301526001600160a01b038381166024840152611c1292610100909204169063ead2b29690604401602060405180830381865afa158015611be7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c0b91906156fa565b600c6117dc565b6005546001600160a01b03908116908216811415611c335761147f82613256565b6000611c3e8361133a565b90506000611c4b836132ba565b90508082121580611c5c5750600082125b15611c6a57611c6a84613256565b50505050565b611130600460019054906101000a90046001600160a01b03166001600160a01b0316632c81c2d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cea9190615696565b611cf261098d565b511115603f6117dc565b6060611d0782541590565b15611d1157919050565b815467ffffffffffffffff811115611d2b57611d2b615745565b604051908082528060200260200182016040528015611d54578160200160208202803683370190505b506000808052600284016020526040812054919250906001600160a01b03165b6001600160a01b03811615611dcd5780838381518110611d9657611d9661575b565b6001600160a01b039092166020928302919091019091015281611db881615771565b9250611dc69050848261333a565b9050611d74565b5050919050565b6000610999611132565b600080611deb8484612281565b90506000611df7610f09565b905080821115611e20576000611e0d82866128bc565b9050611e19818761593d565b9350819250505b509250929050565b6000611e3a60155483101560426117dc565b601654601554611e4a908461593d565b610a0c919061596a565b600060165482611e64919061598c565b601554610a0c91906156c5565b60045460ff16826005811115611e8957611e896155ca565b816005811115611e9b57611e9b6155ca565b14158015611ecb5750816005811115611eb657611eb66155ca565b816005811115611ec857611ec86155ca565b14155b156107bf578060405163683f44bb60e11b81526004016106d091906155e0565b611f45611ef760095490565b6001148015611f3e575060055460008052600b6020527fdf7de25b7f1fd6d0b5205f0e18f1f35bd7b8d84cce336588d184533ce43a6f76546001600160a01b039081169116145b603e6117dc565b611130613370565b6005546001600160a01b03908116908216811461147f576000611f6f8361133a565b90506000611f7c836132ba565b90508082128015611f8e575060008212155b15611c6a57611c6a84613388565b6001611fa781611759565b6117566002612ee7565b600754611fc6906001600160a01b03166133e2565b611fcf816133e2565b6005546040516001600160a01b03909116602482015260009060440160408051601f198184030181529181526020820180516001600160e01b0316639d192aff60e01b1790526007549051631cff79cd60e01b81529192506001600160a01b031690631cff79cd9061204790859085906004016159d7565b6000604051808303816000875af1158015612066573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261208e91908101906158a9565b50600554600754604051636eb1769f60e11b81526001600160a01b03918216600482015230602482015261147f9260001992169063dd62ed3e90604401602060405180830381865afa1580156120e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061210c9190615696565b1460136117dc565b611c6a846323b872dd60e01b858585604051602401612135939291906159fb565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526133f8565b8015806121e65750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156121c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121e49190615696565b155b6122515760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016106d0565b6040516001600160a01b0383166024820152604481018290526107bf90849063095ea7b360e01b90606401612135565b600080600660009054906101000a90046001600160a01b03166001600160a01b031663cd4c8cb26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122fb9190615696565b905061230b8185111560456117dc565b806123195760009150612331565b80612324848661598c565b61232e919061596a565b91505b5092915050565b600061234c61234561163f565b60416117dc565b610999611b10565b61235f6002546134ca565b612368816134d6565b600255565b6004546123879061010090046001600160a01b03166134e4565b612390816133e2565b600480546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b6005546123cd906001600160a01b03166134e4565b60048054604051635bb9b1d160e01b81526001600160a01b038481169382019390935261244f9261010090920490911690635bb9b1d190602401602060405180830381865afa158015612424573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061244891906156fa565b60116117dc565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b600654612486906001600160a01b03166134e4565b61248f816133e2565b600680546001600160a01b0319166001600160a01b0392909216919091179055565b60006124c2612710831060406117dc565b610a0c6124d18361271061306d565b6134f9565b6124e46000821160446117dc565b601655565b6007546124fe906001600160a01b03166134e4565b612507816133e2565b60405163c455279160e01b81523060048201526000906001600160a01b0383169063c455279190602401602060405180830381865afa15801561254e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257291906156dd565b90506001600160a01b0381161561262457612603306001600160a01b0316826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f291906156dd565b6001600160a01b03161460126117dc565b600780546001600160a01b0383166001600160a01b03199091161790555050565b816001600160a01b0316638e1a55fc6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612664573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061268891906156dd565b600780546001600160a01b0319166001600160a01b0392909216918217905561147f906133e2565b6008546126c5906001600160a01b03166134e4565b806001600160a01b031663137d52426040518163ffffffff1660e01b8152600401602060405180830381865afa158015612703573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061272791906156dd565b600880546001600160a01b0319166001600160a01b039290921691909117905550565b600061275581611759565b6117566001612ee7565b6000600460019054906101000a90046001600160a01b03166001600160a01b031663bc0aac106040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127b4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099991906156dd565b60006127e382613577565b6001600160a01b0383166000908152600f60205260408120818155600101559050801561282157600554612821906001600160a01b031683836135e1565b816001600160a01b03167f205acb6266a72dcee37489ca52e143caa5789aed463de89e15336f32191a35018260405161285c91815260200190565b60405180910390a2919050565b6000600260045460ff166005811115612884576128846155ca565b141561289257610999613611565b600360045460ff1660058111156128ab576128ab6155ca565b14156128b657426010555b50600090565b600080600660009054906101000a90046001600160a01b03166001600160a01b031663cd4c8cb26040518163ffffffff1660e01b8152600401602060405180830381865afa158015612912573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129369190615696565b90508061295b576103e8841115612956576129536103e88561593d565b91505b612331565b82612324858361598c565b60045460ff1683600581111561297e5761297e6155ca565b816005811115612990576129906155ca565b141580156129c057508260058111156129ab576129ab6155ca565b8160058111156129bd576129bd6155ca565b14155b80156129ee57508160058111156129d9576129d96155ca565b8160058111156129eb576129eb6155ca565b14155b15611c6a578060405163683f44bb60e11b81526004016106d091906155e0565b6000612a1b82604a6117dc565b6001600160a01b0384166000908152600f6020526040902060010154612a5c57600e546001600160a01b0385166000908152600f6020526040902055612a89565b612a89612a68600e5490565b6001600160a01b0386166000908152600f602052604090205414604b6117dc565b6000612a9361378f565b90506000612710612aa4838261593d565b612aae908761598c565b612ab8919061596a565b90506000612ac6828761593d565b6001600160a01b0388166000908152600f6020526040812060010180549293508492909190612af69084906156c5565b9250508190555081600c6000828254612b0f91906156c5565b9250508190555080600d6000828254612b2891906156c5565b909155505060065460405163bb35783b60e01b81526001600160a01b039091169063bb35783b90612b61908a9030908b906004016159fb565b600060405180830381600087803b158015612b7b57600080fd5b505af1158015612b8f573d6000803e3d6000fd5b505060408051858152602081018590526001600160a01b038b1693507f9003106b80a136fe4cb7a6a0997e6cc436d330bdb86e0e7bac90e8fdb29dec0f92500160405180910390a25060009695505050505050565b600080612bef6137e4565b9050600080612bfe8684611dde565b90925090506000612c0f838861593d565b600654604051632770a7eb60e21b81526001600160a01b038b8116600483015260248201849052929350911690639dc29fac90604401600060405180830381600087803b158015612c5f57600080fd5b505af1158015612c73573d6000803e3d6000fd5b5050505082600014612c9457612c87613808565b612c92888488612a0e565b505b612c9e828561593d565b600754600554919550612cbf916001600160a01b0390811691168a85612114565b612cc884613824565b60408051838152602081018390526001600160a01b038a16917ff3a670cd3af7d64b488926880889d08a8585a138ff455227af6737339a1ec262910160405180910390a2509695505050505050565b600080612d226137e4565b9050612d2f84848361382d565b9150612d3f6000831160476117dc565b6000600360045460ff166005811115612d5a57612d5a6155ca565b148015612d6957504260035414155b15612e3e576000612d7861378f565b9050612d868161271061593d565b612d90828661598c565b612d9a919061596a565b915081600d5411612dad57600d54612daf565b815b915081600d6000828254612dc3919061593d565b909155505060065460405163bb35783b60e01b81526001600160a01b039091169063bb35783b90612dfc9030908a9087906004016159fb565b600060405180830381600087803b158015612e1657600080fd5b505af1158015612e2a573d6000803e3d6000fd5b505050508184612e3a91906156c5565b9350505b612e4884836156c5565b600754600554919350612e6a916001600160a01b039081169188911687612114565b612e738261398a565b60408051858152602081018590529081018290526001600160a01b038616907f2bdd59583c8e5cc64165e86af2482dbe93e85c98b355b788aa592465b3f6920e9060600160405180910390a2505092915050565b60008115612ed7575050600c5490565b600d54600c54610a0c91906156c5565b6004805482919060ff19166001836005811115612f0657612f066155ca565b02179055507e4c016723406921f4b392be09d598b65a291d1d3944775e2eb58e5fd892b7f481604051612f3991906155e0565b60405180910390a150565b600080612f4f61098d565b905060005b8151811015612f8f57612f7f828281518110612f7257612f7261575b565b6020026020010151611f4d565b612f8881615771565b9050612f54565b50600084806020019051810190612fa69190615a1f565b905060005b8151811015612fe657612fd6828281518110612fc957612fc961575b565b6020026020010151611b8d565b612fdf81615771565b9050612fab565b50612fef611c70565b6000612ff9611132565b905061300f61300886836139eb565b600d6117dc565b600360045460ff166005811115613028576130286155ca565b14801561303b575061303b6001826117a8565b1561306457600061304c60016117fc565b9050613056611959565b613060818361593d565b9150505b95945050505050565b60008161307957600080fd5b60006130858484613a87565b905060016001607f1b036001600160801b0382161115610f0257600080fd5b600080600660009054906101000a90046001600160a01b03166001600160a01b03166357be68ef6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156130fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061311e9190615696565b90508061312d57600091505090565b6000613137611dd4565b905061314281613be9565b60005460065460145460405163bb35783b60e01b81526001600160a01b03938416939092169163bb35783b9161317f9160019186916004016159fb565b600060405180830381600087803b15801561319957600080fd5b505af11580156131ad573d6000803e3d6000fd5b505050506131ba82613c32565b60148054600091829055601391909155426017556011546012546131ec91600160801b9004600f90810b91900b613cd0565b601180546001600160801b03928316600160801b0292169190911790556040516001600160a01b038316907f0186322afaa24e5486d6623af5d0e208a455457ad04039dce6adda7c210b5846906132469084815260200190565b60405180910390a2949350505050565b600260036004613267838383612966565b613272600985613cee565b15611c6a576040516001600160a01b03851681527f0e3c58ebfb2e7465fbb1c32e6b4f40c3c4f5ca77e8218a386aff8617831260d7906020015b60405180910390a150505050565b60048054604051633807c0f960e11b81526001600160a01b0384811693820193909352600092610a0c926101009004169063700f81f290602401602060405180830381865afa158015613311573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133359190615696565b613d68565b6001600160a01b038082166000818152600285016020526040812054909216908114613366578061232e565b5060009392505050565b6002600461337e8282611e71565b61147f6005612ee7565b600260036004613399838383612966565b6133a4600985613dd6565b15611c6a576040516001600160a01b03851681527f37803e2125c48ee96c38ddf04e826daf335b0e1603579040fd275aba6d06b6fc906020016132ac565b6117566001600160a01b038216151560156117dc565b600061344d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613e539092919063ffffffff16565b8051909150156107bf578080602001905181019061346b91906156fa565b6107bf5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016106d0565b611756811560146117dc565b6117566000821160156117dc565b6117566001600160a01b0382161560146117dc565b600061354c61354461350e6301e187e0613e62565b61353b61353361352b876135226001613e62565b600f0b90613e80565b600f0b613eb4565b600f0b613eee565b600f0b90613f10565b600f0b613f68565b601180546001600160801b0319166001600160801b03929092169190911790819055600f0b92915050565b6001600160a01b0381166000908152600f602052604081208054600e805484929081106135a6576135a661575b565b906000526020600020906002020190508060000154826001015482600101546135cf919061598c565b6135d9919061596a565b949350505050565b6040516001600160a01b0383166024820152604481018290526107bf90849063a9059cbb60e01b90606401612135565b6000804290506000600660009054906101000a90046001600160a01b03166001600160a01b031663cd4c8cb26040518163ffffffff1660e01b8152600401602060405180830381865afa15801561366c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136909190615696565b905060006136c6826136bd600160401b613522601054886136b1919061593d565b601154600f0b90613fbd565b600f0b906141e0565b905060006136dc6000546001600160a01b031690565b6006546040516340c10f1960e01b81526001600160a01b038084166004830152602482018690529293509116906340c10f1990604401600060405180830381600087803b15801561372c57600080fd5b505af1158015613740573d6000803e3d6000fd5b5050506010859055506040518281526001600160a01b038216907fba0d846635977f8323c35c4fa4b2d1a7e85923e56dc44782bf382d13897e88489060200160405180910390a2509392505050565b6000600460019054906101000a90046001600160a01b03166001600160a01b031663bd6d28506040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f5a573d6000803e3d6000fd5b6000806137ef611132565b90506137f9612869565b5061380381613be9565b919050565b600261381381611759565b61381d6003612ee7565b5042600355565b61175681613c32565b600061383983836128bc565b9050600660009054906101000a90046001600160a01b03166001600160a01b031663cd4c8cb26040518163ffffffff1660e01b8152600401602060405180830381865afa15801561388e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138b29190615696565b61391d576006546040516340c10f1960e01b81526001600160a01b03909116600482018190526103e86024830152906340c10f1990604401600060405180830381600087803b15801561390457600080fd5b505af1158015613918573d6000803e3d6000fd5b505050505b6006546040516340c10f1960e01b81526001600160a01b03868116600483015260248201849052909116906340c10f1990604401600060405180830381600087803b15801561396b57600080fd5b505af115801561397f573d6000803e3d6000fd5b505050509392505050565b61399381613c32565b600360045460ff1660058111156139ac576139ac6155ca565b1480156139bf57506139bf6001826117a8565b15611756576139ce60016117fc565b6139d8908261593d565b90506139e381613c32565b611756611959565b600080612710600460019054906101000a90046001600160a01b03166001600160a01b03166378285c716040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a689190615696565b613a72908661598c565b613a7c919061596a565b909210159392505050565b600081613a9357600080fd5b60006001600160c01b038411613abe5782604085901b81613ab657613ab6615954565b049050613bd5565b60c084811c6401000000008110613ad7576020918201911c5b620100008110613ae9576010918201911c5b6101008110613afa576008918201911c5b60108110613b0a576004918201911c5b60048110613b1a576002918201911c5b60028110613b29576001820191505b60bf820360018603901c6001018260ff0387901b81613b4a57613b4a615954565b0492506001600160801b03831115613b6157600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015613b8d576001820391505b608084901b92900382811015613ba4576001820391505b829003608084901c8214613bba57613bba615ad1565b888181613bc957613bc9615954565b04870196505050505050505b6001600160801b03811115610f0257600080fd5b600260045460ff166005811115613c0257613c026155ca565b1480613c245750600360045460ff166005811115613c2257613c226155ca565b145b156117565761175681614248565b600654604080516357be68ef60e01b815290516000926001600160a01b0316916357be68ef9160048083019260209291908290030181865afa158015613c7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ca09190615696565b9050613cac828261306d565b601280546001600160801b0319166001600160801b03929092169190911790555050565b600081600f0b83600f0b1315613ce7575081610a0c565b5080610a0c565b6001600160a01b03808216600090815260028401602052604081205490911615613d1a57506000610a0c565b60008080526001840160205260409020546001600160a01b0316613d3f8482856144da565b613d4b848460006144da565b8354846000613d5983615771565b91905055506001915050610a0c565b60006001600160ff1b03821115613dd25760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b60648201526084016106d0565b5090565b60006001600160a01b038216613dee57506000610a0c565b6001600160a01b03808316600090815260028501602052604090205416613e1757506000610a0c565b6000613e238484614567565b90506000613e31858561333a565b9050613e3e8583836144da565b613e488585614593565b600192505050610a0c565b60606135d984846000856145e8565b6000677fffffffffffffff821115613e7957600080fd5b5060401b90565b6000600f82810b9084900b0360016001607f1b03198112801590613eab575060016001607f1b038113155b610f0257600080fd5b60008082600f0b13613ec557600080fd5b6080613ed083614719565b600f0b6fb17217f7d1cf79abc9e3b39803f2f6af02901c9050919050565b6000600f82900b60016001607f1b03191415613f0957600080fd5b5060000390565b600081600f0b60001415613f2357600080fd5b600082600f0b604085600f0b901b81613f3e57613f3e615954565b05905060016001607f1b03198112801590613eab575060016001607f1b03811315610f0257600080fd5b6000600160461b82600f0b12613f7d57600080fd5b683fffffffffffffffff1982600f0b1215613f9a57506000919050565b610a0c608083600f0b700171547652b82fe1777d0ffda0d23a7d1202901d6147f4565b600080600084600f0b128015613fd65750826001166001145b905060008085600f0b12613fea5784613fef565b846000035b6001600160801b03169050600160801b600160401b821161408457603f82901b91505b841561407c576001851615614027578102607f1c5b908002607f1c90600285161561403d578102607f1c5b908002607f1c906004851615614053578102607f1c5b908002607f1c906008851615614069578102607f1c5b60049490941c93908002607f1c90614012565b60401c61419a565b603f600160601b83101561409e5760209290921b91601f19015b600160701b8310156140b65760109290921b91600f19015b600160781b8310156140ce5760089290921b91600719015b6001607c1b8310156140e65760049290921b91600319015b6001607e1b8310156140fe5760029290921b91600119015b6001607f1b8310156141165760019290921b91600019015b60005b8615614183576040821061412c57600080fd5b600187161561415257918302607f1c918101600160801b83111561415257600192831c92015b928002607f1c9260019190911b90600160801b841061417757600193841c9391909101905b600187901c9650614119565b6040811061419057600080fd5b6040039190911c90505b6000836141a757816141ac565b816000035b905060016001607f1b031981128015906141cd575060016001607f1b038113155b6141d657600080fd5b9695505050505050565b6000816141ef57506000610a0c565b600083600f0b121561420057600080fd5b600f83900b6001600160801b038316810260401c90608084901c026001600160c01b0381111561422f57600080fd5b60401b811981111561424057600080fd5b019392505050565b600654604080516357be68ef60e01b815290516000926001600160a01b0316916357be68ef9160048083019260209291908290030181865afa158015614292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142b69190615696565b9050806142c1575050565b60006142cd838361306d565b905060006143206142dd84613d68565b6011546012546143179161430091600160801b909104600f90810b91900b613cd0565b60115461352290600160801b9004600f0b87613cd0565b600f0b906152a9565b60125490915060009061433d90600160801b9004600f0b836152a9565b9050614367614362600083614353601354613d68565b61435d9190615ae7565b61536a565b61537b565b6013819055600090614379908761593d565b90506000816013548761438c919061598c565b614396919061596a565b905060145481111561442a576006546014546001600160a01b03909116906340c10f19906001906143c7908561593d565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801561440d57600080fd5b505af1158015614421573d6000803e3d6000fd5b505050506144af565b6006546014546001600160a01b0390911690639dc29fac9060019061445090859061593d565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801561449657600080fd5b505af11580156144aa573d6000803e3d6000fd5b505050505b6014555050601280546001600160801b0319166001600160801b039390931692909217909155505050565b6001600160a01b038116156144ef57806144f1565b815b6001600160a01b038381166000818152600287016020526040902080546001600160a01b031916939092169290921790551561452d578161452f565b805b6001600160a01b039182166000908152600194909401602052604090932080546001600160a01b031916939091169290921790915550565b6001600160a01b038082166000818152600185016020526040812054909216908114613366578061232e565b6001600160a01b0381166000908152600183016020908152604080832080546001600160a01b03199081169091556002860190925282208054909116905582549083906145df83615b28565b91905055505050565b6060824710156146495760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016106d0565b6001600160a01b0385163b6146a05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016106d0565b600080866001600160a01b031685876040516146bc9190615b3f565b60006040518083038185875af1925050503d80600081146146f9576040519150601f19603f3d011682016040523d82523d6000602084013e6146fe565b606091505b509150915061470e8282866153cd565b979650505050505050565b60008082600f0b1361472a57600080fd5b6000600f83900b600160401b8112614744576040918201911d5b6401000000008112614758576020918201911d5b62010000811261476a576010918201911d5b610100811261477b576008918201911d5b6010811261478b576004918201911d5b6004811261479b576002918201911d5b600281126147aa576001820191505b603f19820160401b600f85900b607f8490031b6001603f1b5b60008113156147e95790800260ff81901c8281029390930192607f011c9060011d6147c3565b509095945050505050565b6000600160461b82600f0b1261480957600080fd5b683fffffffffffffffff1982600f0b121561482657506000919050565b6001607f1b60006001603f1b8416600f0b13156148545770016a09e667f3bcc908b2fb1366ea957d3e0260801c5b60008367400000000000000016600f0b1315614881577001306fe0a31b7152de8d5a46305c85edec0260801c5b60008367200000000000000016600f0b13156148ae577001172b83c7d517adcdf7c8c50eb14a791f0260801c5b60008367100000000000000016600f0b13156148db5770010b5586cf9890f6298b92b71842a983630260801c5b60008367080000000000000016600f0b1315614908577001059b0d31585743ae7c548eb68ca417fd0260801c5b60008367040000000000000016600f0b131561493557700102c9a3e778060ee6f7caca4f7a29bde80260801c5b60008367020000000000000016600f0b13156149625770010163da9fb33356d84a66ae336dcdfa3f0260801c5b60008367010000000000000016600f0b131561498f57700100b1afa5abcbed6129ab13ec11dc95430260801c5b600083668000000000000016600f0b13156149bb5770010058c86da1c09ea1ff19d294cf2f679b0260801c5b600083664000000000000016600f0b13156149e7577001002c605e2e8cec506d21bfc89a23a00f0260801c5b600083662000000000000016600f0b1315614a1357700100162f3904051fa128bca9c55c31e5df0260801c5b600083661000000000000016600f0b1315614a3f577001000b175effdc76ba38e31671ca9397250260801c5b600083660800000000000016600f0b1315614a6b57700100058ba01fb9f96d6cacd4b180917c3d0260801c5b600083660400000000000016600f0b1315614a975770010002c5cc37da9491d0985c348c68e7b30260801c5b600083660200000000000016600f0b1315614ac3577001000162e525ee054754457d59952920260260801c5b600083660100000000000016600f0b1315614aef5770010000b17255775c040618bf4a4ade83fc0260801c5b6000836580000000000016600f0b1315614b1a577001000058b91b5bc9ae2eed81e9b7d4cfab0260801c5b6000836540000000000016600f0b1315614b4557700100002c5c89d5ec6ca4d7c8acc017b7c90260801c5b6000836520000000000016600f0b1315614b705770010000162e43f4f831060e02d839a9d16d0260801c5b6000836510000000000016600f0b1315614b9b57700100000b1721bcfc99d9f890ea069117630260801c5b6000836508000000000016600f0b1315614bc65770010000058b90cf1e6d97f9ca14dbcc16280260801c5b6000836504000000000016600f0b1315614bf1577001000002c5c863b73f016468f6bac5ca2b0260801c5b6000836502000000000016600f0b1315614c1c57700100000162e430e5a18f6119e3c02282a50260801c5b6000836501000000000016600f0b1315614c47577001000000b1721835514b86e6d96efd1bfe0260801c5b60008364800000000016600f0b1315614c7157700100000058b90c0b48c6be5df846c5b2ef0260801c5b60008364400000000016600f0b1315614c9b5770010000002c5c8601cc6b9e94213c72737a0260801c5b60008364200000000016600f0b1315614cc5577001000000162e42fff037df38aa2b219f060260801c5b60008364100000000016600f0b1315614cef5770010000000b17217fba9c739aa5819f44f90260801c5b60008364080000000016600f0b1315614d19577001000000058b90bfcdee5acd3c1cedc8230260801c5b60008364040000000016600f0b1315614d4357700100000002c5c85fe31f35a6a30da1be500260801c5b60008364020000000016600f0b1315614d6d5770010000000162e42ff0999ce3541b9fffcf0260801c5b60008364010000000016600f0b1315614d9757700100000000b17217f80f4ef5aadda455540260801c5b600083638000000016600f0b1315614dc05770010000000058b90bfbf8479bd5a81b51ad0260801c5b600083634000000016600f0b1315614de9577001000000002c5c85fdf84bd62ae30a74cc0260801c5b600083632000000016600f0b1315614e1257700100000000162e42fefb2fed257559bdaa0260801c5b600083631000000016600f0b1315614e3b577001000000000b17217f7d5a7716bba4a9ae0260801c5b600083630800000016600f0b1315614e6457700100000000058b90bfbe9ddbac5e109cce0260801c5b600083630400000016600f0b1315614e8d5770010000000002c5c85fdf4b15de6f17eb0d0260801c5b600083630200000016600f0b1315614eb6577001000000000162e42fefa494f1478fde050260801c5b600083630100000016600f0b1315614edf5770010000000000b17217f7d20cf927c8e94c0260801c5b6000836280000016600f0b1315614f07577001000000000058b90bfbe8f71cb4e4b33d0260801c5b6000836240000016600f0b1315614f2f57700100000000002c5c85fdf477b662b269450260801c5b6000836220000016600f0b1315614f575770010000000000162e42fefa3ae53369388c0260801c5b6000836210000016600f0b1315614f7f57700100000000000b17217f7d1d351a389d400260801c5b6000836208000016600f0b1315614fa75770010000000000058b90bfbe8e8b2d3d4ede0260801c5b6000836204000016600f0b1315614fcf577001000000000002c5c85fdf4741bea6e77e0260801c5b6000836202000016600f0b1315614ff757700100000000000162e42fefa39fe95583c20260801c5b6000836201000016600f0b131561501f577001000000000000b17217f7d1cfb72b45e10260801c5b60008361800016600f0b131561504657700100000000000058b90bfbe8e7cc35c3f00260801c5b60008361400016600f0b131561506d5770010000000000002c5c85fdf473e242ea380260801c5b60008361200016600f0b1315615094577001000000000000162e42fefa39f02b772c0260801c5b60008361100016600f0b13156150bb5770010000000000000b17217f7d1cf7d83c1a0260801c5b60008361080016600f0b13156150e2577001000000000000058b90bfbe8e7bdcbe2e0260801c5b60008361040016600f0b131561510957700100000000000002c5c85fdf473dea871f0260801c5b60008361020016600f0b13156151305770010000000000000162e42fefa39ef44d910260801c5b60008361010016600f0b131561515757700100000000000000b17217f7d1cf79e9490260801c5b600083608016600f0b131561517d5770010000000000000058b90bfbe8e7bce5440260801c5b600083604016600f0b13156151a3577001000000000000002c5c85fdf473de6eca0260801c5b600083602016600f0b13156151c957700100000000000000162e42fefa39ef366f0260801c5b600083601016600f0b13156151ef577001000000000000000b17217f7d1cf79afa0260801c5b600083600816600f0b131561521557700100000000000000058b90bfbe8e7bcd6d0260801c5b600083600416600f0b131561523b5770010000000000000002c5c85fdf473de6b20260801c5b600083600216600f0b1315615261577001000000000000000162e42fefa39ef3580260801c5b600083600116600f0b13156152875770010000000000000000b17217f7d1cf79ab0260801c5b600f83810b60401d603f03900b1c60016001607f1b03811115610a0c57600080fd5b6000600f83900b60016001607f1b031914156152f2576002600160c01b031982121580156152db5750600160c01b8213155b6152e457600080fd5b506000819003603f1b610a0c565b60008084600f0b121561530a57836000039350600190505b600083121561531c5760009290920391155b600061532885856141e0565b9050811561534d57600160ff1b81111561534157600080fd5b6000039150610a0c9050565b6001600160ff1b0381111561536157600080fd5b9150610a0c9050565b600081831315613ce7575081610a0c565b600080821215613dd25760405162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f73697469766560448201526064016106d0565b606083156153dc575081610f02565b8251156153ec5782518084602001fd5b8160405162461bcd60e51b81526004016106d09190615b5b565b6000806020838503121561541957600080fd5b823567ffffffffffffffff8082111561543157600080fd5b818501915085601f83011261544557600080fd5b81358181111561545457600080fd5b86602082850101111561546657600080fd5b60209290920196919550909350505050565b60006020828403121561548a57600080fd5b5035919050565b6001600160a01b038116811461175657600080fd5b6000602082840312156154b857600080fd5b8135610f0281615491565b600081518084526020808501945080840160005b838110156154fc5781516001600160a01b0316875295820195908201906001016154d7565b509495945050505050565b602081526000610f0260208301846154c3565b600080600080600080600080610100898b03121561553757600080fd5b88359750602089013561554981615491565b9650604089013561555981615491565b9550606089013561556981615491565b94506080890135935060a0890135925060c0890135915060e089013561558e81615491565b809150509295985092959890939650565b801515811461175657600080fd5b6000602082840312156155bf57600080fd5b8135610f028161559f565b634e487b7160e01b600052602160045260246000fd5b60208101600683106155f4576155f46155ca565b91905290565b6000806040838503121561560d57600080fd5b82359150602083013561561f8161559f565b809150509250929050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6000602082840312156156a857600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600082198211156156d8576156d86156af565b500190565b6000602082840312156156ef57600080fd5b8151610f0281615491565b60006020828403121561570c57600080fd5b8151610f028161559f565b6000806040838503121561572a57600080fd5b82516157358161559f565b6020939093015192949293505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600019821415615785576157856156af565b5060010190565b60608152600061579f60608301866154c3565b82810360208481019190915285518083528682019282019060005b818110156157d6578451835293830193918301916001016157ba565b50506001600160a01b0395909516604094909401939093525091949350505050565b60208101604d83106155f4576155f46155ca565b6001600160a01b03841681526040602082018190528101829052818360608301376000818301606090810191909152601f909201601f1916010192915050565b604051601f8201601f1916810167ffffffffffffffff8111828210171561587557615875615745565b604052919050565b60005b83811015615898578181015183820152602001615880565b83811115611c6a5750506000910152565b6000602082840312156158bb57600080fd5b815167ffffffffffffffff808211156158d357600080fd5b818401915084601f8301126158e757600080fd5b8151818111156158f9576158f9615745565b61590c601f8201601f191660200161584c565b915080825285602082850101111561592357600080fd5b61593481602084016020860161587d565b50949350505050565b60008282101561594f5761594f6156af565b500390565b634e487b7160e01b600052601260045260246000fd5b60008261598757634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156159a6576159a66156af565b500290565b600081518084526159c381602086016020860161587d565b601f01601f19169290920160200192915050565b6001600160a01b03831681526040602082018190526000906135d9908301846159ab565b6001600160a01b039384168152919092166020820152604081019190915260600190565b60006020808385031215615a3257600080fd5b825167ffffffffffffffff80821115615a4a57600080fd5b818501915085601f830112615a5e57600080fd5b815181811115615a7057615a70615745565b8060051b9150615a8184830161584c565b8181529183018401918481019088841115615a9b57600080fd5b938501935b83851015615ac55784519250615ab583615491565b8282529385019390850190615aa0565b98975050505050505050565b634e487b7160e01b600052600160045260246000fd5b600080821280156001600160ff1b0384900385131615615b0957615b096156af565b600160ff1b8390038412811615615b2257615b226156af565b50500190565b600081615b3757615b376156af565b506000190190565b60008251615b5181846020870161587d565b9190910192915050565b602081526000610f0260208301846159ab56fea2646970667358221220964ede387e7541732eb1f194b7f25ab83a3cd7076f84adc1cb4c3a4c927d5fbe64736f6c634300080a0033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.