Contract 0x83f105A3c6d83Fd90255057dCd08798335443F96

 
 
Txn Hash
Method
Block
From
To
Value [Txn Fee]
0x6bd7928e32fbb90289dbb6ed2a7b60a842df67c07a8721d01db0eb54ff3064d10x61010060290800862022-06-02 16:14:51371 days 19 hrs agoFurucombo: Deployer IN  Create: AFurucombo0 MATIC0.061928408138 35.579056788
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AFurucombo

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 26 : 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 2 of 26 : 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 3 of 26 : 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 4 of 26 : ActionBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Usdt} from "../interfaces/IERC20Usdt.sol";
import {AssetQuotaAction} from "../utils/AssetQuotaAction.sol";
import {DealingAssetAction} from "../utils/DealingAssetAction.sol";

/// @title Action base of external protocols
abstract contract ActionBase is AssetQuotaAction, DealingAssetAction {
    using SafeERC20 for IERC20;

    // prettier-ignore
    address public constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /// @notice Get token balance.
    /// @param token_ The token address.
    /// @return The balance of token.
    function _getBalance(address token_) internal view returns (uint256) {
        return _getBalanceWithAmount(token_, type(uint256).max);
    }

    /// @notice Get token balance with amount.
    /// @param token_ The token address.
    /// @param amount_ The amount of token.
    /// @return The balance of token if `amount_` is maximum value otherwise `amount_`.
    function _getBalanceWithAmount(address token_, uint256 amount_) internal view returns (uint256) {
        if (amount_ != type(uint256).max) {
            return amount_;
        }

        // Native token
        if (token_ == NATIVE_TOKEN_ADDRESS) {
            return address(this).balance;
        }
        // ERC20 token
        return IERC20(token_).balanceOf(address(this));
    }

    /// @notice Approve the token amount.
    /// @param token_ The token address.
    /// @param spender_ The spender address.
    /// @param amount_ The approve amount of token.
    function _tokenApprove(
        address token_,
        address spender_,
        uint256 amount_
    ) internal {
        try IERC20Usdt(token_).approve(spender_, amount_) {} catch {
            IERC20(token_).safeApprove(spender_, 0);
            IERC20(token_).safeApprove(spender_, amount_);
        }
    }
}

File 5 of 26 : AFurucombo.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {DestructibleAction} from "../../utils/DestructibleAction.sol";
import {DelegateCallAction} from "../../utils/DelegateCallAction.sol";
import {Errors} from "../../utils/Errors.sol";
import {IFund} from "../../interfaces/IFund.sol";
import {ActionBase} from "../ActionBase.sol";
import {IComptroller} from "../../interfaces/IComptroller.sol";
import {IDebtToken} from "../../interfaces/IDebtToken.sol";
import {IFurucombo} from "./IFurucombo.sol";

/// @title The action of Furucombo
contract AFurucombo is ActionBase, DestructibleAction, DelegateCallAction {
    using SafeERC20 for IERC20;

    address payable public immutable proxy;
    IComptroller public immutable comptroller;
    uint256 private constant _TOKEN_DUST = 10;

    constructor(
        address payable owner_,
        address payable proxy_,
        address comptroller_
    ) DestructibleAction(owner_) DelegateCallAction() {
        proxy = proxy_;
        comptroller = IComptroller(comptroller_);
    }

    /// @notice Inject tokens and execute combo.
    /// @param tokensIn_ The input tokens.
    /// @param amountsIn_ The input token amounts.
    /// @param tokensOut_ The sorted output tokens
    /// @param tos_ The handlers of combo.
    /// @param configs_ The configurations of executing cubes.
    /// @param datas_ The combo datas.
    /// @return The output token amounts.
    function injectAndBatchExec(
        address[] calldata tokensIn_,
        uint256[] calldata amountsIn_,
        address[] calldata tokensOut_,
        address[] calldata tos_,
        bytes32[] calldata configs_,
        bytes[] memory datas_
    ) external payable delegateCallOnly returns (uint256[] memory) {
        // check comptroller handler call
        _checkHandlerCall(tos_, datas_);

        // Inject and execute combo
        _inject(tokensIn_, amountsIn_);

        // Snapshot output token amounts after send token to Furucombo proxy
        uint256[] memory amountsOut = new uint256[](tokensOut_.length);
        for (uint256 i = 0; i < tokensOut_.length; i++) {
            // Check duplicate tokens out
            if (i > 0) {
                Errors._require(tokensOut_[i] > tokensOut_[i - 1], Errors.Code.AFURUCOMBO_DUPLICATED_TOKENSOUT);
            }

            // Get balance before execution
            amountsOut[i] = _getBalance(tokensOut_[i]);
        }

        // Execute furucombo proxy batchExec
        try IFurucombo(proxy).batchExec(tos_, configs_, datas_) returns (address[] memory dealingAssets) {
            for (uint256 i = 0; i < dealingAssets.length; i++) {
                // Update dealing asset
                _addDealingAsset(dealingAssets[i]);
            }
        } catch Error(string memory reason) {
            Errors._revertMsg("injectAndBatchExec", reason);
        } catch {
            Errors._revertMsg("injectAndBatchExec");
        }

        // Check no remaining input tokens to ensure updateTokens was called
        for (uint256 i = 0; i < tokensIn_.length; i++) {
            Errors._require(
                IERC20(tokensIn_[i]).balanceOf(proxy) < _TOKEN_DUST,
                Errors.Code.AFURUCOMBO_REMAINING_TOKENS
            );
        }

        // Calculate increased output token amounts
        for (uint256 i = 0; i < tokensOut_.length; i++) {
            amountsOut[i] = _getBalance(tokensOut_[i]) - amountsOut[i];

            // Update asset quota
            _increaseAssetQuota(tokensOut_[i], amountsOut[i]);
        }

        return amountsOut;
    }

    function approveDelegation(IDebtToken[] calldata tokens, uint256[] calldata amounts)
        external
        payable
        delegateCallOnly
    {
        Errors._require(tokens.length == amounts.length, Errors.Code.AFURUCOMBO_TOKENS_AND_AMOUNTS_LENGTH_INCONSISTENT);

        // approve delegation to furucombo proxy only,
        // otherwise manager can borrow tokens base on the collateral of funds
        for (uint256 i = 0; i < tokens.length; i++) {
            try tokens[i].approveDelegation(proxy, amounts[i]) {
                // Update dealing asset
                _addDealingAsset(address(tokens[i]));
            } catch Error(string memory reason) {
                Errors._revertMsg("approveDelegation", reason);
            } catch {
                Errors._revertMsg("approveDelegation");
            }
        }
    }

    function approveToken(address[] calldata tokens, uint256[] calldata amounts) external payable delegateCallOnly {
        Errors._require(tokens.length == amounts.length, Errors.Code.AFURUCOMBO_TOKENS_AND_AMOUNTS_LENGTH_INCONSISTENT);

        // approve token to furucombo proxy only,
        // otherwise manager can approve tokens to other address
        for (uint256 i = 0; i < tokens.length; i++) {
            _tokenApprove(tokens[i], proxy, amounts[i]);
            _addDealingAsset(tokens[i]);
        }
    }

    /// @notice verify valid handler .
    function _checkHandlerCall(address[] memory tos_, bytes[] memory datas_) internal {
        // check comptroller handler call
        uint256 level = IFund(msg.sender).level();
        for (uint256 i = 0; i < tos_.length; ++i) {
            Errors._require(
                comptroller.canHandlerCall(level, tos_[i], bytes4(datas_[i])),
                Errors.Code.AFURUCOMBO_INVALID_COMPTROLLER_HANDLER_CALL
            );
        }
    }

    /// @notice Inject tokens to furucombo.
    function _inject(address[] memory tokensIn_, uint256[] memory amountsIn_) internal {
        Errors._require(
            tokensIn_.length == amountsIn_.length,
            Errors.Code.AFURUCOMBO_TOKENS_AND_AMOUNTS_LENGTH_INCONSISTENT
        );

        for (uint256 i = 0; i < tokensIn_.length; i++) {
            uint256 amount = amountsIn_[i];

            if (amount > 0) {
                // decrease asset quota
                _decreaseAssetQuota(tokensIn_[i], amount);
                IERC20(tokensIn_[i]).safeTransfer(proxy, amount);
            }
        }
    }
}

File 6 of 26 : IFurucombo.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

interface IFurucombo {
    function batchExec(
        address[] calldata tos_,
        bytes32[] calldata configs_,
        bytes[] memory datas_
    ) external payable returns (address[] memory);
}

File 7 of 26 : 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 8 of 26 : 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 9 of 26 : 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 10 of 26 : 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 11 of 26 : 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 12 of 26 : IDebtToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IDebtToken {
    function balanceOf(address user) external view returns (uint256);

    function scaledBalanceOf(address user) external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function scaledTotalSupply() external view returns (uint256);

    function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);

    function approveDelegation(address delegatee, uint256 amount) external;

    function borrowAllowance(address fromUser, address toUser) external view returns (uint256);

    function transfer(address recipient, uint256 amount) external returns (bool);

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);
}

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

interface IERC20Usdt {
    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount) external;

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 amount) external;

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external;

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(address indexed owner, address indexed spender, uint256 value);
}

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

interface IFund {
    function level() external returns (uint256);

    function vault() external view returns (address);
}

File 15 of 26 : 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 16 of 26 : 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 17 of 26 : AssetQuota.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {StorageArray} from "./StorageArray.sol";
import {StorageMap} from "./StorageMap.sol";

library AssetQuota {
    using StorageArray for bytes32;
    using StorageMap for bytes32;

    // Data is stored in storage slot `uint256(keccak256('furucombo.funds.quota.map')) - 1`, so that it doesn't
    // conflict with the storage layout of the implementation behind the proxy.
    bytes32 private constant _QUOTA_MAP_SLOT = 0x1af59a3fd3f5a4bba6259b5a65dd4f4fbaab48545aeeabdfb60969120dbd5c35;

    // Data is stored in storage slot `uint256(keccak256('furucombo.funds.quota.array')) - 1`, so that it doesn't
    // conflict with the storage layout of the implementation behind the proxy.
    bytes32 private constant _QUOTA_ARR_SLOT = 0x041334f809138adff4aed76ee4e45b3671e485ee2dcac112682c24d3a0c21736;

    function _get(address key_) internal view returns (uint256) {
        bytes32 key = bytes32(bytes20(key_));
        return uint256(_QUOTA_MAP_SLOT._get(key));
    }

    function _set(address key_, uint256 val_) internal {
        bytes32 key = bytes32(bytes20(key_));
        uint256 oldVal = uint256(_QUOTA_MAP_SLOT._get(key));
        if (oldVal == 0) {
            _QUOTA_ARR_SLOT._push(key);
        }

        bytes32 val = bytes32(val_);
        _QUOTA_MAP_SLOT._set(key, val);
    }

    function _clean() internal {
        for (uint256 i = 0; i < _QUOTA_ARR_SLOT._getLength(); i++) {
            bytes32 key = _QUOTA_ARR_SLOT._get(i);
            _QUOTA_MAP_SLOT._set(key, 0);
        }
        _QUOTA_ARR_SLOT._delete();
    }
}

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

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

library DealingAsset {
    using StorageArray for bytes32;

    // Data is stored in storage slot `uint256(keccak256('furucombo.funds.asset.array')) - 1`, so that it doesn't
    // conflict with the storage layout of the implementation behind the proxy.
    bytes32 private constant _ASSET_ARR_SLOT = 0x25241bfd865dc0cf716378d03594b4104571b985a2d5cf72950d41c4b7474874;

    function _add(address asset_) internal {
        if (!_exist(asset_)) {
            bytes32 asset = bytes32(bytes20(asset_));
            _ASSET_ARR_SLOT._push(asset);
        }
    }

    function _clean() internal {
        _ASSET_ARR_SLOT._delete();
    }

    function _assets() internal view returns (address[] memory) {
        uint256 length = _ASSET_ARR_SLOT._getLength();
        address[] memory assets = new address[](length);
        for (uint256 i = 0; i < length; i++) {
            assets[i] = address(bytes20(_ASSET_ARR_SLOT._get(i)));
        }
        return assets;
    }

    function _getLength() internal view returns (uint256) {
        return _ASSET_ARR_SLOT._getLength();
    }

    function _exist(address asset_) internal view returns (bool) {
        for (uint256 i = 0; i < _ASSET_ARR_SLOT._getLength(); i++) {
            if (asset_ == address(bytes20(_ASSET_ARR_SLOT._get(i)))) {
                return true;
            }
        }
        return false;
    }
}

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

library StorageArray {
    struct Slot {
        bytes32 value;
    }

    function _getSlot(bytes32 slot_) private pure returns (Slot storage ret) {
        assembly {
            ret.slot := slot_
        }
    }

    function _get(bytes32 slot_, uint256 index_) internal view returns (bytes32 val) {
        require(index_ < uint256(_getSlot(slot_).value), "StorageArray: _get invalid index");
        uint256 s = uint256(keccak256(abi.encodePacked(slot_))) + index_;
        val = _getSlot(bytes32(s)).value;
    }

    function _set(
        bytes32 slot_,
        uint256 index_,
        bytes32 val_
    ) internal {
        require(index_ < uint256(_getSlot(slot_).value), "StorageArray: _set invalid index");
        uint256 s = uint256(keccak256(abi.encodePacked(slot_))) + index_;
        _getSlot(bytes32(s)).value = val_;
    }

    function _push(bytes32 slot_, bytes32 val_) internal {
        uint256 length = uint256(_getSlot(slot_).value);
        _getSlot(slot_).value = bytes32(length + 1);
        _set(slot_, length, val_);
    }

    function _pop(bytes32 slot_) internal returns (bytes32 val) {
        uint256 length = uint256(_getSlot(slot_).value);
        length -= 1;
        uint256 s = uint256(keccak256(abi.encodePacked(slot_))) + length;
        val = _getSlot(bytes32(s)).value;
        _getSlot(slot_).value = bytes32(length);
    }

    function _getLength(bytes32 slot_) internal view returns (uint256) {
        return uint256(_getSlot(slot_).value);
    }

    function _delete(bytes32 slot_) internal {
        delete _getSlot(slot_).value;
    }
}

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

library StorageMap {
    struct Slot {
        bytes32 value;
    }

    function _getSlot(bytes32 slot_) private pure returns (Slot storage ret) {
        assembly {
            ret.slot := slot_
        }
    }

    function _get(bytes32 slot_, bytes32 key_) internal view returns (bytes32 ret) {
        bytes32 b = keccak256(abi.encodePacked(key_, slot_));
        ret = _getSlot(b).value;
    }

    function _set(
        bytes32 slot_,
        bytes32 key_,
        bytes32 val_
    ) internal {
        bytes32 b = keccak256(abi.encodePacked(key_, slot_));
        _getSlot(b).value = val_;
    }
}

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

import {AssetQuota} from "../libraries/AssetQuota.sol";

/**
 * @dev Create immutable owner for action contract
 */
abstract contract AssetQuotaAction {
    modifier quotaCleanUp() {
        _cleanAssetQuota();
        _;
        _cleanAssetQuota();
    }

    function _getAssetQuota(address asset_) internal view returns (uint256) {
        return AssetQuota._get(asset_);
    }

    function _isAssetQuotaZero(address asset_) internal view returns (bool) {
        return _getAssetQuota(asset_) == 0;
    }

    function _setAssetQuota(address asset_, uint256 quota_) internal {
        AssetQuota._set(asset_, quota_);
    }

    function _increaseAssetQuota(address asset_, uint256 quota_) internal {
        uint256 oldQuota = AssetQuota._get(asset_);
        _setAssetQuota(asset_, oldQuota + quota_);
    }

    function _decreaseAssetQuota(address asset_, uint256 quota_) internal {
        uint256 oldQuota = AssetQuota._get(asset_);
        _setAssetQuota(asset_, oldQuota - quota_);
    }

    function _cleanAssetQuota() internal {
        AssetQuota._clean();
    }
}

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

import {DealingAsset} from "../libraries/DealingAsset.sol";

/**
 * @dev Create immutable owner for action contract
 */
abstract contract DealingAssetAction {
    modifier assetCleanUp() {
        _cleanAssets();
        _;
        _cleanAssets();
    }

    function _isDealingAssetExist(address asset_) internal view returns (bool) {
        return DealingAsset._exist(asset_);
    }

    function _getDealingAssets() internal view returns (address[] memory) {
        return DealingAsset._assets();
    }

    function _getDealingAssetLength() internal view returns (uint256) {
        return DealingAsset._getLength();
    }

    function _addDealingAsset(address asset_) internal {
        DealingAsset._add(asset_);
    }

    function _cleanAssets() internal {
        DealingAsset._clean();
    }
}

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

/**
 * @dev Can only be delegate call.
 */
abstract contract DelegateCallAction {
    address private immutable _self;

    modifier delegateCallOnly() {
        require(_self != address(this), "Delegate call only");
        _;
    }

    constructor() {
        _self = address(this);
    }
}

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

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

/**
 * @dev Can only be destroyed by owner. All funds are sent to the owner.
 */
abstract contract DestructibleAction is OwnableAction {
    constructor(address payable owner_) OwnableAction(owner_) {}

    function destroy() external {
        require(msg.sender == actionOwner, "DestructibleAction: caller is not the owner");
        selfdestruct(actionOwner);
    }
}

File 25 of 26 : 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");
    }
}

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

/**
 * @dev Create immutable owner for action contract
 */
abstract contract OwnableAction {
    address payable public immutable actionOwner;

    constructor(address payable owner_) {
        actionOwner = owner_;
    }
}

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":[{"internalType":"address payable","name":"owner_","type":"address"},{"internalType":"address payable","name":"proxy_","type":"address"},{"internalType":"address","name":"comptroller_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"enum Errors.Code","name":"errorCode","type":"uint8"}],"name":"RevertCode","type":"error"},{"inputs":[],"name":"NATIVE_TOKEN_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"actionOwner","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDebtToken[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"approveDelegation","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"approveToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract IComptroller","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"destroy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokensIn_","type":"address[]"},{"internalType":"uint256[]","name":"amountsIn_","type":"uint256[]"},{"internalType":"address[]","name":"tokensOut_","type":"address[]"},{"internalType":"address[]","name":"tos_","type":"address[]"},{"internalType":"bytes32[]","name":"configs_","type":"bytes32[]"},{"internalType":"bytes[]","name":"datas_","type":"bytes[]"}],"name":"injectAndBatchExec","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"proxy","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

6101006040523480156200001257600080fd5b5060405162001fc438038062001fc4833981016040819052620000359162000070565b6001600160a01b039283166080523060a05290821660c0521660e052620000c4565b6001600160a01b03811681146200006d57600080fd5b50565b6000806000606084860312156200008657600080fd5b8351620000938162000057565b6020850151909350620000a68162000057565b6040850151909250620000b98162000057565b809150509250925092565b60805160a05160c05160e051611e8162000143600039600081816101030152610b4e01526000818161019c01528181610411015281816105bf015281816108620152818161097f0152610cb90152600081816101c2015281816107d701526108e901526000818160920152818161072801526107ab0152611e816000f3fe60806040526004361061007b5760003560e01c80638a4cecc31161004e5780638a4cecc31461013c578063be0b188f1461014f578063df2ebdbb14610162578063ec5568891461018a57600080fd5b8063275d7a261461008057806328849140146100d15780635fe3b567146100f157806383197ef014610125575b600080fd5b34801561008c57600080fd5b506100b47f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100e46100df36600461181e565b6101be565b6040516100c89190611934565b3480156100fd57600080fd5b506100b47f000000000000000000000000000000000000000000000000000000000000000081565b34801561013157600080fd5b5061013a61071d565b005b61013a61014a366004611978565b6107d5565b61013a61015d366004611978565b6108e7565b34801561016e57600080fd5b506100b473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b34801561019657600080fd5b506100b47f000000000000000000000000000000000000000000000000000000000000000081565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163014156102125760405162461bcd60e51b8152600401610209906119e4565b60405180910390fd5b610250868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250610ad5915050565b6102ce8c8c80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508b8b80806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250610c4f92505050565b60008767ffffffffffffffff8111156102e9576102e96116c6565b604051908082528060200260200182016040528015610312578160200160208202803683370190505b50905060005b888110156103f957801561039b5761039b8a8a610336600185611a26565b81811061034557610345611a3d565b905060200201602081019061035a9190611a68565b6001600160a01b03168b8b8481811061037557610375611a3d565b905060200201602081019061038a9190611a68565b6001600160a01b0316116024610d23565b6103ca8a8a838181106103b0576103b0611a3d565b90506020020160208101906103c59190611a68565b610d47565b8282815181106103dc576103dc611a3d565b6020908102919091010152806103f181611a85565b915050610318565b50604051631c62e04760e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906338c5c08e9061044e908a908a908a908a908a90600401611b4d565b6000604051808303816000875af192505050801561048e57506040513d6000823e601f3d908101601f1916820160405261048b9190810190611bed565b60015b61052e5761049a611c87565b806308c379a014156104f457506104af611ca3565b806104ba57506104f6565b6104ee60405180604001604052806012815260200171696e6a656374416e6442617463684578656360701b81525082610d5b565b50610571565b505b61052960405180604001604052806012815260200171696e6a656374416e6442617463684578656360701b815250610d94565b610571565b60005b815181101561056e5761055c82828151811061054f5761054f611a3d565b6020026020010151610dc4565b8061056681611a85565b915050610531565b50505b60005b8c81101561064e5761063c600a8f8f8481811061059357610593611a3d565b90506020020160208101906105a89190611a68565b6040516370a0823160e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015291909116906370a0823190602401602060405180830381865afa158015610610573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106349190611d2d565b106025610d23565b8061064681611a85565b915050610574565b5060005b8881101561070c5781818151811061066c5761066c611a3d565b60200260200101516106898b8b848181106103b0576103b0611a3d565b6106939190611a26565b8282815181106106a5576106a5611a3d565b6020026020010181815250506106fa8a8a838181106106c6576106c6611a3d565b90506020020160208101906106db9190611a68565b8383815181106106ed576106ed611a3d565b6020026020010151610dcd565b8061070481611a85565b915050610652565b509c9b505050505050505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146107a95760405162461bcd60e51b815260206004820152602b60248201527f446573747275637469626c65416374696f6e3a2063616c6c6572206973206e6f60448201526a3a103a34329037bbb732b960a91b6064820152608401610209565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316ff5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630141561081e5760405162461bcd60e51b8152600401610209906119e4565b61082b8382146026610d23565b60005b838110156108e05761089f85858381811061084b5761084b611a3d565b90506020020160208101906108609190611a68565b7f000000000000000000000000000000000000000000000000000000000000000085858581811061089357610893611a3d565b90506020020135610ded565b6108ce8585838181106108b4576108b4611a3d565b90506020020160208101906108c99190611a68565b610dc4565b806108d881611a85565b91505061082e565b5050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163014156109305760405162461bcd60e51b8152600401610209906119e4565b61093d8382146026610d23565b60005b838110156108e05784848281811061095a5761095a611a3d565b905060200201602081019061096f9190611a68565b6001600160a01b031663c04a8a107f00000000000000000000000000000000000000000000000000000000000000008585858181106109b0576109b0611a3d565b6040516001600160e01b031960e087901b1681526001600160a01b0390941660048501526020029190910135602483015250604401600060405180830381600087803b1580156109ff57600080fd5b505af1925050508015610a10575060015b610aae57610a1c611c87565b806308c379a01415610a755750610a31611ca3565b80610a3c5750610a77565b610a6f6040518060400160405280601181526020017030b8383937bb32a232b632b3b0ba34b7b760791b81525082610d5b565b50610ac3565b505b610aa96040518060400160405280601181526020017030b8383937bb32a232b632b3b0ba34b7b760791b815250610d94565b610ac3565b610ac38585838181106108b4576108b4611a3d565b80610acd81611a85565b915050610940565b6000336001600160a01b0316636fd5ae156040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610b17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3b9190611d2d565b905060005b8351811015610c4957610c397f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637ded7edf84878581518110610b8e57610b8e611a3d565b6020026020010151878681518110610ba857610ba8611a3d565b6020026020010151610bb990611d46565b6040516001600160e01b031960e086901b8116825260048201949094526001600160a01b0390921660248301529091166044820152606401602060405180830381865afa158015610c0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c329190611d7d565b6027610d23565b610c4281611a85565b9050610b40565b50505050565b610c5e81518351146026610d23565b60005b8251811015610d1e576000828281518110610c7e57610c7e611a3d565b602002602001015190506000811115610d0b57610cb4848381518110610ca657610ca6611a3d565b602002602001015182610e75565b610d0b7f000000000000000000000000000000000000000000000000000000000000000082868581518110610ceb57610ceb611a3d565b60200260200101516001600160a01b0316610e909092919063ffffffff16565b5080610d1681611a85565b915050610c61565b505050565b81610d43578060405163193106d160e21b81526004016102099190611d9f565b5050565b6000610d5582600019610ef3565b92915050565b8181604051602001610d6e929190611dc7565b60408051601f198184030181529082905262461bcd60e51b825261020991600401611e04565b610dc1816040518060400160405280600b81526020016a155b9cdc1958da599a595960aa1b815250610d5b565b50565b610dc181610fa0565b6000610dd883610fe6565b9050610d1e83610de88484611e17565b611021565b60405163095ea7b360e01b81526001600160a01b0383811660048301526024820183905284169063095ea7b390604401600060405180830381600087803b158015610e3757600080fd5b505af1925050508015610e48575060015b610d1e57610e616001600160a01b03841683600061102b565b610d1e6001600160a01b038416838361102b565b6000610e8083610fe6565b9050610d1e83610de88484611a26565b6040516001600160a01b038316602482015260448101829052610d1e90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611140565b60006000198214610f05575080610d55565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415610f31575047610d55565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015610f75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f999190611d2d565b9392505050565b610fa981611212565b610dc1576001600160601b0319606082901b16610d437f25241bfd865dc0cf716378d03594b4104571b985a2d5cf72950d41c4b7474874826112a9565b60006001600160601b0319606083901b16610f997f1af59a3fd3f5a4bba6259b5a65dd4f4fbaab48545aeeabdfb60969120dbd5c35826112c3565b610d43828261130a565b8015806110a55750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa15801561107f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a39190611d2d565b155b6111105760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610209565b6040516001600160a01b038316602482015260448101829052610d1e90849063095ea7b360e01b90606401610ebc565b6000611195826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166113a29092919063ffffffff16565b805190915015610d1e57808060200190518101906111b39190611d7d565b610d1e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610209565b6000805b7f25241bfd865dc0cf716378d03594b4104571b985a2d5cf72950d41c4b7474874548110156112a0576112697f25241bfd865dc0cf716378d03594b4104571b985a2d5cf72950d41c4b7474874826113b9565b60601c6001600160a01b0316836001600160a01b0316141561128e5750600192915050565b8061129881611a85565b915050611216565b50600092915050565b81546112b6816001611e17565b8355610d1e83828461144c565b60008082846040516020016112e2929190918252602082015260400190565b6040516020818303038152906040528051906020012090506113018190565b54949350505050565b6001600160601b0319606083901b1660006113457f1af59a3fd3f5a4bba6259b5a65dd4f4fbaab48545aeeabdfb60969120dbd5c35836112c3565b905080611376576113767f041334f809138adff4aed76ee4e45b3671e485ee2dcac112682c24d3a0c21736836112a9565b826108e07f1af59a3fd3f5a4bba6259b5a65dd4f4fbaab48545aeeabdfb60969120dbd5c3584836114e1565b60606113b18484600085611510565b949350505050565b60008254821061140b5760405162461bcd60e51b815260206004820181905260248201527f53746f7261676541727261793a205f67657420696e76616c696420696e6465786044820152606401610209565b6000828460405160200161142191815260200190565b6040516020818303038152906040528051906020012060001c6114449190611e17565b905080611301565b8254821061149c5760405162461bcd60e51b815260206004820181905260248201527f53746f7261676541727261793a205f73657420696e76616c696420696e6465786044820152606401610209565b600082846040516020016114b291815260200190565b6040516020818303038152906040528051906020012060001c6114d59190611e17565b905081815b5550505050565b6040805160208082018590528183018690528251808303840181526060909201909252805191012081816114da565b6060824710156115715760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610209565b6001600160a01b0385163b6115c85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610209565b600080866001600160a01b031685876040516115e49190611e2f565b60006040518083038185875af1925050503d8060008114611621576040519150601f19603f3d011682016040523d82523d6000602084013e611626565b606091505b5091509150611636828286611641565b979650505050505050565b60608315611650575081610f99565b8251156116605782518084602001fd5b8160405162461bcd60e51b81526004016102099190611e04565b60008083601f84011261168c57600080fd5b50813567ffffffffffffffff8111156116a457600080fd5b6020830191508360208260051b85010111156116bf57600080fd5b9250929050565b634e487b7160e01b600052604160045260246000fd5b601f8201601f1916810167ffffffffffffffff81118282101715611702576117026116c6565b6040525050565b600067ffffffffffffffff821115611723576117236116c6565b5060051b60200190565b6000601f838184011261173f57600080fd5b8235602061174c82611709565b6040805161175a83826116dc565b84815260059490941b870183019383810192508885111561177a57600080fd5b8388015b8581101561181157803567ffffffffffffffff8082111561179f5760008081fd5b818b0191508b603f8301126117b45760008081fd5b86820135818111156117c8576117c86116c6565b855191506117de818b01601f19168901836116dc565b8082528c868285010111156117f35760008081fd5b8086840189840137600090820188015285525092840192840161177e565b5098975050505050505050565b600080600080600080600080600080600060c08c8e03121561183f57600080fd5b67ffffffffffffffff808d35111561185657600080fd5b6118638e8e358f0161167a565b909c509a5060208d013581101561187957600080fd5b6118898e60208f01358f0161167a565b909a50985060408d013581101561189f57600080fd5b6118af8e60408f01358f0161167a565b909850965060608d01358110156118c557600080fd5b6118d58e60608f01358f0161167a565b909650945060808d01358110156118eb57600080fd5b6118fb8e60808f01358f0161167a565b909450925060a08d013581101561191157600080fd5b506119228d60a08e01358e0161172d565b90509295989b509295989b9093969950565b6020808252825182820181905260009190848201906040850190845b8181101561196c57835183529284019291840191600101611950565b50909695505050505050565b6000806000806040858703121561198e57600080fd5b843567ffffffffffffffff808211156119a657600080fd5b6119b28883890161167a565b909650945060208701359150808211156119cb57600080fd5b506119d88782880161167a565b95989497509550505050565b60208082526012908201527144656c65676174652063616c6c206f6e6c7960701b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082821015611a3857611a38611a10565b500390565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0381168114610dc157600080fd5b600060208284031215611a7a57600080fd5b8135610f9981611a53565b6000600019821415611a9957611a99611a10565b5060010190565b60005b83811015611abb578181015183820152602001611aa3565b83811115610c495750506000910152565b60008151808452611ae4816020860160208601611aa0565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b85811015611b40578284038952611b2e848351611acc565b98850198935090840190600101611b16565b5091979650505050505050565b6060808252810185905260008660808301825b88811015611b90578235611b7381611a53565b6001600160a01b0316825260209283019290910190600101611b60565b5083810360208501528581526001600160fb1b03861115611bb057600080fd5b8560051b91508187602083013781810191505060208101600081526020848303016040850152611be08186611af8565b9998505050505050505050565b60006020808385031215611c0057600080fd5b825167ffffffffffffffff811115611c1757600080fd5b8301601f81018513611c2857600080fd5b8051611c3381611709565b604051611c4082826116dc565b82815260059290921b8301840191848101915087831115611c6057600080fd5b928401925b82841015611636578351611c7881611a53565b82529284019290840190611c65565b600060033d1115611ca05760046000803e5060005160e01c5b90565b600060443d1015611cb15790565b6040516003193d81016004833e81513d67ffffffffffffffff8160248401118184111715611ce157505050505090565b8285019150815181811115611cf95750505050505090565b843d8701016020828501011115611d135750505050505090565b611d22602082860101876116dc565b509095945050505050565b600060208284031215611d3f57600080fd5b5051919050565b805160208201516001600160e01b03198082169291906004831015611d755780818460040360031b1b83161693505b505050919050565b600060208284031215611d8f57600080fd5b81518015158114610f9957600080fd5b60208101604d8310611dc157634e487b7160e01b600052602160045260246000fd5b91905290565b60008351611dd9818460208801611aa0565b6101d160f51b9083019081528351611df8816002840160208801611aa0565b01600201949350505050565b602081526000610f996020830184611acc565b60008219821115611e2a57611e2a611a10565b500190565b60008251611e41818460208701611aa0565b919091019291505056fea2646970667358221220cf75dd1a00d2d6888536b251055883ce288d49de1095da5e985d48553eafb6f664736f6c634300080a003300000000000000000000000064585922a9703d9ede7d353a6522eb2970f750660000000000000000000000006f2ea968ae4d50c0dbd5020c148a5eeb87e4f8cb00000000000000000000000024fdb881efaaf200c29c7449fb16845cc081bab7

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

00000000000000000000000064585922a9703d9ede7d353a6522eb2970f750660000000000000000000000006f2ea968ae4d50c0dbd5020c148a5eeb87e4f8cb00000000000000000000000024fdb881efaaf200c29c7449fb16845cc081bab7

-----Decoded View---------------
Arg [0] : owner_ (address): 0x64585922a9703d9ede7d353a6522eb2970f75066
Arg [1] : proxy_ (address): 0x6f2ea968ae4d50c0dbd5020c148a5eeb87e4f8cb
Arg [2] : comptroller_ (address): 0x24fdb881efaaf200c29c7449fb16845cc081bab7

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000064585922a9703d9ede7d353a6522eb2970f75066
Arg [1] : 0000000000000000000000006f2ea968ae4d50c0dbd5020c148a5eeb87e4f8cb
Arg [2] : 00000000000000000000000024fdb881efaaf200c29c7449fb16845cc081bab7


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.