Contract 0x5464020009fbC03a931C79ACC062C6C677Ccb1C1

 
 
Txn Hash
Method
Block
From
To
Value [Txn Fee]
0xa8ffbbac4d193eb5e89ae9d4888c98ee10fac68d2d03894d58eb61449d4ebc4f0x60e06040290799072022-06-02 16:08:41368 days 3 hrs agoFurucombo: Deployer IN  Create: TaskExecutor0 MATIC0.090051861108 51.189999999
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
TaskExecutor

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 24 : 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 24 : 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 24 : 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 24 : TaskExecutor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
pragma experimental ABIEncoderV2;

import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ITaskExecutor} from "./interfaces/ITaskExecutor.sol";
import {IComptroller} from "./interfaces/IComptroller.sol";
import {IFund} from "./interfaces/IFund.sol";
import {Errors} from "./utils/Errors.sol";
import {DestructibleAction} from "./utils/DestructibleAction.sol";
import {DelegateCallAction} from "./utils/DelegateCallAction.sol";
import {AssetQuotaAction} from "./utils/AssetQuotaAction.sol";
import {DealingAssetAction} from "./utils/DealingAssetAction.sol";
import {LibParam} from "./libraries/LibParam.sol";

/// @title The fund action task executor
contract TaskExecutor is ITaskExecutor, DestructibleAction, DelegateCallAction, AssetQuotaAction, DealingAssetAction {
    using Address for address;
    using SafeERC20 for IERC20;
    using LibParam for bytes32;

    // prettier-ignore
    address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    uint256 public constant PERCENTAGE_BASE = 1 ether;
    uint256 private constant _FEE_BASE = 1e4;
    IComptroller public immutable comptroller;

    event ExecFee(address indexed fund, address indexed token, uint256 fee);

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

    /// @notice Task execution function, will charge execution fee first.
    /// @param tokensIn_ The list of tokens used in execution.
    /// @param amountsIn_ The amount of tokens used in execution.
    /// @param tos_ The address of action.
    /// @param configs_ The configurations of executing actions.
    /// @param datas_ The action datas.
    /// @return The address of dealing asset list.
    /// inheritdoc ITaskExecutor, DelegateCallAction, AssetQuotaAction, DealingAssetAction.
    function batchExec(
        address[] calldata tokensIn_,
        uint256[] calldata amountsIn_,
        address[] calldata tos_,
        bytes32[] calldata configs_,
        bytes[] memory datas_
    ) external payable delegateCallOnly quotaCleanUp assetCleanUp returns (address[] memory) {
        _chargeExecutionFee(tokensIn_, amountsIn_);
        return _execs(tos_, configs_, datas_);
    }

    /// @notice The execution phase.
    /// @param tos_ The address of action.
    /// @param configs_ The configurations of executing actions.
    /// @param datas_ The action datas.
    /// @return The address of dealing asset list.
    function _execs(
        address[] memory tos_,
        bytes32[] memory configs_,
        bytes[] memory datas_
    ) internal returns (address[] memory) {
        bytes32[256] memory localStack;
        uint256 index = 0;

        Errors._require(tos_.length == datas_.length, Errors.Code.TASK_EXECUTOR_TOS_AND_DATAS_LENGTH_INCONSISTENT);
        Errors._require(tos_.length == configs_.length, Errors.Code.TASK_EXECUTOR_TOS_AND_CONFIGS_LENGTH_INCONSISTENT);

        uint256 level = IFund(msg.sender).level();

        for (uint256 i = 0; i < tos_.length; i++) {
            bytes32 config = configs_[i];

            if (config._isDelegateCall()) {
                // check comptroller delegate call
                Errors._require(
                    comptroller.canDelegateCall(level, tos_[i], bytes4(datas_[i])),
                    Errors.Code.TASK_EXECUTOR_INVALID_COMPTROLLER_DELEGATE_CALL
                );

                // Trim params from local stack depend on config
                _trimParams(datas_[i], config, localStack, index);

                // Execute action by delegate call
                bytes memory result = tos_[i].functionDelegateCall(
                    datas_[i],
                    "TaskExecutor: low-level delegate call failed"
                ); // use openzeppelin address delegate call, use error message directly

                // Store return data from action to local stack
                index = _parseReturn(result, config, localStack, index);
            } else {
                // Decode eth value from data
                (uint256 ethValue, bytes memory _data) = _decodeEthValue(datas_[i]);

                // check comptroller contract call
                Errors._require(
                    comptroller.canContractCall(level, tos_[i], bytes4(_data)),
                    Errors.Code.TASK_EXECUTOR_INVALID_COMPTROLLER_CONTRACT_CALL
                );

                // Trim params from local stack depend on config
                _trimParams(_data, config, localStack, index);

                // Execute action by call
                bytes memory result = tos_[i].functionCallWithValue(
                    _data,
                    ethValue,
                    "TaskExecutor: low-level call with value failed"
                ); // use openzeppelin address value call, use error message directly

                // Store return data from action to local stack depend on config
                index = _parseReturn(result, config, localStack, index);
            }
        }

        // verify dealing assets
        address[] memory dealingAssets = _getDealingAssets();
        Errors._require(
            comptroller.isValidDealingAssets(level, dealingAssets),
            Errors.Code.TASK_EXECUTOR_INVALID_DEALING_ASSET
        );
        return dealingAssets;
    }

    /// @notice Trimming the execution parameter if needed.
    /// @param data_ The execution data.
    /// @param config_ The configuration.
    /// @param localStack_ The stack the be referenced.
    /// @param index_ Current element count of localStack.
    function _trimParams(
        bytes memory data_,
        bytes32 config_,
        bytes32[256] memory localStack_,
        uint256 index_
    ) internal pure {
        if (config_._isStatic()) {
            // Don't need to trim parameters if static
            return;
        }

        // Trim the execution data base on the configuration and stack content if dynamic
        // Fetch the parameter configuration from config
        (uint256[] memory refs, uint256[] memory params) = config_._getParams();

        // Trim the data with the reference and parameters
        for (uint256 i = 0; i < refs.length; i++) {
            Errors._require(refs[i] < index_, Errors.Code.TASK_EXECUTOR_REFERENCE_TO_OUT_OF_LOCALSTACK);
            bytes32 ref = localStack_[refs[i]];
            uint256 offset = params[i];
            uint256 base = PERCENTAGE_BASE;
            assembly {
                let loc := add(add(data_, 0x20), offset)
                let m := mload(loc)
                // Adjust the value by multiplier if a dynamic parameter is not zero
                if iszero(iszero(m)) {
                    // Assert no overflow first
                    let p := mul(m, ref)
                    if iszero(eq(div(p, m), ref)) {
                        revert(0, 0)
                    } // require(p / m == ref)
                    ref := div(p, base)
                }
                mstore(loc, ref)
            }
        }
    }

    /// @notice Parse the execution return data into the local stack if needed.
    /// @param ret_ The return data.
    /// @param config_ The configuration.
    /// @param localStack_ The local stack to place the return values.
    /// @param index_ The current tail.
    /// @return The new index.
    function _parseReturn(
        bytes memory ret_,
        bytes32 config_,
        bytes32[256] memory localStack_,
        uint256 index_
    ) internal pure returns (uint256) {
        if (config_._isReferenced()) {
            // If so, parse the output and place it into local stack
            uint256 num = config_._getReturnNum();
            uint256 newIndex = _parse(localStack_, ret_, index_);
            Errors._require(
                newIndex == index_ + num,
                Errors.Code.TASK_EXECUTOR_RETURN_NUM_AND_PARSED_RETURN_NUM_NOT_MATCHED
            );
            index_ = newIndex;
        }
        return index_;
    }

    /// @notice Parse the return data into the local stack.
    /// @param localStack_ The local stack to place the return values.
    /// @param ret_ The return data.
    /// @param index_ The current tail.
    /// @return newIndex The new index.
    function _parse(
        bytes32[256] memory localStack_,
        bytes memory ret_,
        uint256 index_
    ) internal pure returns (uint256 newIndex) {
        uint256 len = ret_.length;
        // The return value should be multiple of 32-bytes to be parsed.
        Errors._require(len % 32 == 0, Errors.Code.TASK_EXECUTOR_ILLEGAL_LENGTH_FOR_PARSE);
        // Estimate the tail after the process.
        newIndex = index_ + len / 32;
        Errors._require(newIndex <= 256, Errors.Code.TASK_EXECUTOR_STACK_OVERFLOW);
        assembly {
            let offset := shl(5, index_)
            // Store the data into localStack
            for {
                let i := 0
            } lt(i, len) {
                i := add(i, 0x20)
            } {
                mstore(add(localStack_, add(i, offset)), mload(add(add(ret_, i), 0x20)))
            }
        }
    }

    /// @notice Decode eth value from the execution data.
    /// @param data_ The execution data.
    /// @return The first return uint256 value mean eth value,
    ///         the second return bytes value means execution data.
    function _decodeEthValue(bytes memory data_) internal pure returns (uint256, bytes memory) {
        return abi.decode(data_, (uint256, bytes));
    }

    /// @notice Charge execution fee from input tokens.
    /// @param tokensIn_ The input tokens.
    /// @param amountsIn_ The input token amounts.
    function _chargeExecutionFee(address[] calldata tokensIn_, uint256[] calldata amountsIn_) internal {
        // Check initial asset from white list
        uint256 level = IFund(msg.sender).level();
        Errors._require(
            comptroller.isValidInitialAssets(level, tokensIn_),
            Errors.Code.TASK_EXECUTOR_INVALID_INITIAL_ASSET
        );

        // collect execution fee to collector
        uint256 feePercentage = comptroller.execFeePercentage();
        address payable collector = payable(comptroller.execFeeCollector());

        for (uint256 i = 0; i < tokensIn_.length; i++) {
            // make sure all quota should be zero at the begin
            Errors._require(_isAssetQuotaZero(tokensIn_[i]), Errors.Code.TASK_EXECUTOR_NON_ZERO_QUOTA);

            // send fee to collector
            uint256 execFee = (amountsIn_[i] * feePercentage) / _FEE_BASE;
            IERC20(tokensIn_[i]).safeTransfer(collector, execFee);
            _setAssetQuota(tokensIn_[i], amountsIn_[i] - execFee);

            emit ExecFee(msg.sender, tokensIn_[i], execFee);
        }
    }
}

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

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

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

library LibParam {
    bytes32 private constant _STATIC_MASK = 0x0100000000000000000000000000000000000000000000000000000000000000;
    bytes32 private constant _CALLTYPE_MASK = 0x0200000000000000000000000000000000000000000000000000000000000000;
    bytes32 private constant _PARAMS_MASK = 0x0000000000000000000000000000000000000000000000000000000000000001;
    bytes32 private constant _REFS_MASK = 0x00000000000000000000000000000000000000000000000000000000000000FF;
    bytes32 private constant _RETURN_NUM_MASK = 0x00FF000000000000000000000000000000000000000000000000000000000000;

    uint256 private constant _REFS_LIMIT = 22;
    uint256 private constant _PARAMS_SIZE_LIMIT = 64;
    uint256 private constant _RETURN_NUM_OFFSET = 240;

    function _isStatic(bytes32 conf_) internal pure returns (bool) {
        return (conf_ & _STATIC_MASK == 0);
    }

    function _isReferenced(bytes32 conf_) internal pure returns (bool) {
        return !(_getReturnNum(conf_) == 0);
    }

    function _isDelegateCall(bytes32 conf_) internal pure returns (bool) {
        return (conf_ & _CALLTYPE_MASK == 0);
    }

    function _getReturnNum(bytes32 conf_) internal pure returns (uint256 num) {
        bytes32 temp = (conf_ & _RETURN_NUM_MASK) >> _RETURN_NUM_OFFSET;
        num = uint256(temp);
    }

    function _getParams(bytes32 conf_) internal pure returns (uint256[] memory refs, uint256[] memory params) {
        require(!_isStatic(conf_), "Static params");
        uint256 n = _REFS_LIMIT;
        while (conf_ & _REFS_MASK == _REFS_MASK && n > 0) {
            n--;
            conf_ = conf_ >> 8;
        }
        require(n > 0, "No dynamic param");
        refs = new uint256[](n);
        params = new uint256[](n);
        for (uint256 i = 0; i < n; i++) {
            refs[i] = uint256(conf_ & _REFS_MASK);
            conf_ = conf_ >> 8;
        }
        uint256 _i = 0;
        for (uint256 k = 0; k < _PARAMS_SIZE_LIMIT; k++) {
            if (conf_ & _PARAMS_MASK != 0) {
                require(_i < n, "Location count exceeds ref count");
                params[_i] = k * 32 + 4;
                _i++;
            }
            conf_ = conf_ >> 1;
        }
        require(_i == n, "Location count less than ref count");
    }
}

File 17 of 24 : 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 18 of 24 : 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 19 of 24 : 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 20 of 24 : 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 21 of 24 : 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 22 of 24 : 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 23 of 24 : 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 24 of 24 : 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","name":"comptroller_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"enum Errors.Code","name":"errorCode","type":"uint8"}],"name":"RevertCode","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fund","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"ExecFee","type":"event"},{"inputs":[],"name":"NATIVE_TOKEN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENTAGE_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"actionOwner","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokensIn_","type":"address[]"},{"internalType":"uint256[]","name":"amountsIn_","type":"uint256[]"},{"internalType":"address[]","name":"tos_","type":"address[]"},{"internalType":"bytes32[]","name":"configs_","type":"bytes32[]"},{"internalType":"bytes[]","name":"datas_","type":"bytes[]"}],"name":"batchExec","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"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"}]

60e06040523480156200001157600080fd5b5060405162001fe738038062001fe7833981016040819052620000349162000069565b6001600160a01b039182166080523060a0521660c052620000a8565b6001600160a01b03811681146200006657600080fd5b50565b600080604083850312156200007d57600080fd5b82516200008a8162000050565b60208401519092506200009d8162000050565b809150509250929050565b60805160a05160c051611ed96200010e6000396000818160e5015281816103df01528181610479015281816104ff015281816107ed015281816109910152610ae10152600061016c015260008181606c0152818161029201526103150152611ed96000f3fe6080604052600436106100555760003560e01c8063275d7a261461005a57806331f7d964146100ab5780635fe3b567146100d35780636912f45a1461010757806383197ef01461012757806387c139431461013e575b600080fd5b34801561006657600080fd5b5061008e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100b757600080fd5b5061008e73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b3480156100df57600080fd5b5061008e7f000000000000000000000000000000000000000000000000000000000000000081565b61011a61011536600461195d565b610168565b6040516100a29190611a88565b34801561013357600080fd5b5061013c610287565b005b34801561014a57600080fd5b5061015a670de0b6b3a764000081565b6040519081526020016100a2565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163014156101dd5760405162461bcd60e51b815260206004820152601260248201527144656c65676174652063616c6c206f6e6c7960701b60448201526064015b60405180910390fd5b6101e561033f565b6101ed610349565b6101f98a8a8a8a610372565b61026886868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808a02828101820190935289825290935089925088918291850190849080828437600092019190915250879250610719915050565b9050610272610349565b61027a61033f565b9998505050505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146103135760405162461bcd60e51b815260206004820152602b60248201527f446573747275637469626c65416374696f6e3a2063616c6c6572206973206e6f60448201526a3a103a34329037bbb732b960a91b60648201526084016101d4565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316ff5b610347610b82565b565b61034760007f25241bfd865dc0cf716378d03594b4104571b985a2d5cf72950d41c4b747487455565b6000336001600160a01b0316636fd5ae156040518163ffffffff1660e01b81526004016020604051808303816000875af11580156103b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103d89190611a9b565b90506104757f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638404a9228388886040518463ffffffff1660e01b815260040161042d93929190611acc565b602060405180830381865afa15801561044a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046e9190611b24565b6022610bfa565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b392d0d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f99190611a9b565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633f33d8f76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561055b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061057f9190611b46565b905060005b8681101561070f576105c56105be8989848181106105a4576105a4611b63565b90506020020160208101906105b99190611b79565b610c1e565b6023610bfa565b6000612710848888858181106105dd576105dd611b63565b905060200201356105ee9190611bac565b6105f89190611be1565b905061063683828b8b8681811061061157610611611b63565b90506020020160208101906106269190611b79565b6001600160a01b03169190610c30565b61068989898481811061064b5761064b611b63565b90506020020160208101906106609190611b79565b8289898681811061067357610673611b63565b905060200201356106849190611bf5565b610c87565b88888381811061069b5761069b611b63565b90506020020160208101906106b09190611b79565b6001600160a01b0316336001600160a01b03167fdea327bacbf83fde61405778ae514d462e50b30ba152de1758a28027c3c15e27836040516106f491815260200190565b60405180910390a3508061070781611c0c565b915050610584565b5050505050505050565b606061072361179f565b600061073484518751146019610bfa565b6107438551875114601a610bfa565b6000336001600160a01b0316636fd5ae156040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610785573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a99190611a9b565b905060005b8751811015610acf5760008782815181106107cb576107cb611b63565b602002602001015190506107e381600160f91b161590565b15610963576108be7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663434e6645858c868151811061082d5761082d611b63565b60200260200101518b878151811061084757610847611b63565b602002602001015161085890611c27565b6040518463ffffffff1660e01b815260040161087693929190611c5e565b602060405180830381865afa158015610893573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b79190611b24565b601b610bfa565b6108e38783815181106108d3576108d3611b63565b6020026020010151828787610c91565b600061094d8884815181106108fa576108fa611b63565b60200260200101516040518060600160405280602c8152602001611e58602c91398c868151811061092d5761092d611b63565b60200260200101516001600160a01b0316610d8c9092919063ffffffff16565b905061095b81838888610e69565b945050610abc565b60008061098889858151811061097b5761097b611b63565b6020026020010151610eb1565b91509150610a497f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633197fff5878e88815181106109d1576109d1611b63565b6020026020010151856109e390611c27565b6040518463ffffffff1660e01b8152600401610a0193929190611c5e565b602060405180830381865afa158015610a1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a429190611b24565b601c610bfa565b610a5581848989610c91565b6000610aa882846040518060600160405280602e8152602001611e0a602e91398f8981518110610a8757610a87611b63565b60200260200101516001600160a01b0316610ed2909392919063ffffffff16565b9050610ab681858a8a610e69565b96505050505b5080610ac781611c0c565b9150506107ae565b506000610ada611003565b9050610b757f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634b44cc7d84846040518363ffffffff1660e01b8152600401610b2d929190611c87565b602060405180830381865afa158015610b4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6e9190611b24565b601d610bfa565b93505050505b9392505050565b60005b600080516020611e3883398151915254811015610be5576000610bb6600080516020611e3883398151915283611012565b9050610bd2600080516020611e848339815191528260006110aa565b5080610bdd81611c0c565b915050610b85565b506000600080516020611e3883398151915255565b81610c1a578060405163193106d160e21b81526004016101d49190611ca0565b5050565b6000610c29826110dc565b1592915050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610c829084906110ed565b505050565b610c1a82826111bf565b600160f81b8316610ca157610d86565b600080610cad8561122d565b9150915060005b8251811015610d8257610ce384848381518110610cd357610cd3611b63565b602002602001015110601e610bfa565b600085848381518110610cf857610cf8611b63565b60200260200101516101008110610d1157610d11611b63565b602002015190506000838381518110610d2c57610d2c611b63565b602002602001015190506000670de0b6b3a764000090508160208b010180518015610d68578481028582820414610d6257600080fd5b83900494505b509290925250819050610d7a81611c0c565b915050610cb4565b5050505b50505050565b60606001600160a01b0384163b610df45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084016101d4565b600080856001600160a01b031685604051610e0f9190611cf4565b600060405180830381855af49150503d8060008114610e4a576040519150601f19603f3d011682016040523d82523d6000602084013e610e4f565b606091505b5091509150610e5f8282866114dd565b9695505050505050565b600060f084901c60ff1615610ea95760ff60f085901c166000610e8d858886611516565b9050610ea5610e9c8386611d10565b8214601f610bfa565b9250505b509392505050565b6000606082806020019051810190610ec99190611d28565b91509150915091565b606082471015610f335760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016101d4565b6001600160a01b0385163b610f8a5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016101d4565b600080866001600160a01b03168587604051610fa69190611cf4565b60006040518083038185875af1925050503d8060008114610fe3576040519150601f19603f3d011682016040523d82523d6000602084013e610fe8565b606091505b5091509150610ff88282866114dd565b979650505050505050565b606061100d611585565b905090565b6000825482106110645760405162461bcd60e51b815260206004820181905260248201527f53746f7261676541727261793a205f67657420696e76616c696420696e64657860448201526064016101d4565b6000828460405160200161107a91815260200190565b6040516020818303038152906040528051906020012060001c61109d9190611d10565b9050805b54949350505050565b6040805160208082018590528183018690528251808303840181526060909201909252805191012081815b5550505050565b60006110e782611675565b92915050565b6000611142826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166116a39092919063ffffffff16565b805190915015610c8257808060200190518101906111609190611b24565b610c825760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016101d4565b6bffffffffffffffffffffffff19606083901b1660006111ed600080516020611e84833981519152836116ba565b90508061120c5761120c600080516020611e38833981519152836116f8565b82611226600080516020611e8483398151915284836110aa565b5050505050565b606080600160f81b83166112735760405162461bcd60e51b815260206004820152600d60248201526c53746174696320706172616d7360981b60448201526064016101d4565b60165b60ff8481161480156112885750600081115b156112a6578061129781611dab565b915050600884901c9350611276565b600081116112e95760405162461bcd60e51b815260206004820152601060248201526f4e6f2064796e616d696320706172616d60801b60448201526064016101d4565b8067ffffffffffffffff8111156113025761130261180b565b60405190808252806020026020018201604052801561132b578160200160208202803683370190505b5092508067ffffffffffffffff8111156113475761134761180b565b604051908082528060200260200182016040528015611370578160200160208202803683370190505b50915060005b818110156113bb57835160ff86169085908390811061139757611397611b63565b602090810291909101015260089490941c93806113b381611c0c565b915050611376565b506000805b604081101561147b576001861615611462578282106114215760405162461bcd60e51b815260206004820181905260248201527f4c6f636174696f6e20636f756e7420657863656564732072656620636f756e7460448201526064016101d4565b61142c816020611bac565b611437906004611d10565b84838151811061144957611449611b63565b60209081029190910101528161145e81611c0c565b9250505b60019590951c948061147381611c0c565b9150506113c0565b508181146114d65760405162461bcd60e51b815260206004820152602260248201527f4c6f636174696f6e20636f756e74206c657373207468616e2072656620636f756044820152611b9d60f21b60648201526084016101d4565b5050915091565b606083156114ec575081610b7b565b8251156114fc5782518084602001fd5b8160405162461bcd60e51b81526004016101d49190611dc2565b8151600090611531611529602083611df5565b156020610bfa565b61153c602082611be1565b6115469084611d10565b91506115586101008311156021610bfa565b8260051b60005b8281101561157b5760208187018101518383018901520161155f565b5050509392505050565b606060006115b17f25241bfd865dc0cf716378d03594b4104571b985a2d5cf72950d41c4b74748745490565b905060008167ffffffffffffffff8111156115ce576115ce61180b565b6040519080825280602002602001820160405280156115f7578160200160208202803683370190505b50905060005b8281101561166e5761162f7f25241bfd865dc0cf716378d03594b4104571b985a2d5cf72950d41c4b747487482611012565b60601c82828151811061164457611644611b63565b6001600160a01b03909216602092830291909101909101528061166681611c0c565b9150506115fd565b5092915050565b60006bffffffffffffffffffffffff19606083901b16610b7b600080516020611e84833981519152826116ba565b60606116b28484600085610ed2565b949350505050565b60008082846040516020016116d9929190918252602082015260400190565b6040516020818303038152906040528051906020012090506110a18190565b8154611705816001611d10565b8355610c828382848254821061175d5760405162461bcd60e51b815260206004820181905260248201527f53746f7261676541727261793a205f73657420696e76616c696420696e64657860448201526064016101d4565b6000828460405160200161177391815260200190565b6040516020818303038152906040528051906020012060001c6117969190611d10565b905081816110d5565b604051806120000160405280610100906020820280368337509192915050565b60008083601f8401126117d157600080fd5b50813567ffffffffffffffff8111156117e957600080fd5b6020830191508360208260051b850101111561180457600080fd5b9250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561184a5761184a61180b565b604052919050565b600067ffffffffffffffff82111561186c5761186c61180b565b50601f01601f191660200190565b600082601f83011261188b57600080fd5b8135602067ffffffffffffffff808311156118a8576118a861180b565b8260051b6118b7838201611821565b93845285810183019383810190888611156118d157600080fd5b84880192505b85831015611951578235848111156118ef5760008081fd5b8801603f81018a136119015760008081fd5b85810135604061191861191383611852565b611821565b8281528c8284860101111561192d5760008081fd5b828285018a83013760009281018901929092525083525091840191908401906118d7565b98975050505050505050565b600080600080600080600080600060a08a8c03121561197b57600080fd5b893567ffffffffffffffff8082111561199357600080fd5b61199f8d838e016117bf565b909b50995060208c01359150808211156119b857600080fd5b6119c48d838e016117bf565b909950975060408c01359150808211156119dd57600080fd5b6119e98d838e016117bf565b909750955060608c0135915080821115611a0257600080fd5b611a0e8d838e016117bf565b909550935060808c0135915080821115611a2757600080fd5b50611a348c828d0161187a565b9150509295985092959850929598565b600081518084526020808501945080840160005b83811015611a7d5781516001600160a01b031687529582019590820190600101611a58565b509495945050505050565b602081526000610b7b6020830184611a44565b600060208284031215611aad57600080fd5b5051919050565b6001600160a01b0381168114611ac957600080fd5b50565b83815260406020808301829052908201839052600090849060608401835b86811015611b18578335611afd81611ab4565b6001600160a01b031682529282019290820190600101611aea565b50979650505050505050565b600060208284031215611b3657600080fd5b81518015158114610b7b57600080fd5b600060208284031215611b5857600080fd5b8151610b7b81611ab4565b634e487b7160e01b600052603260045260246000fd5b600060208284031215611b8b57600080fd5b8135610b7b81611ab4565b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615611bc657611bc6611b96565b500290565b634e487b7160e01b600052601260045260246000fd5b600082611bf057611bf0611bcb565b500490565b600082821015611c0757611c07611b96565b500390565b6000600019821415611c2057611c20611b96565b5060010190565b805160208201516001600160e01b03198082169291906004831015611c565780818460040360031b1b83161693505b505050919050565b9283526001600160a01b039190911660208301526001600160e01b031916604082015260600190565b8281526040602082015260006116b26040830184611a44565b60208101604d8310611cc257634e487b7160e01b600052602160045260246000fd5b91905290565b60005b83811015611ce3578181015183820152602001611ccb565b83811115610d865750506000910152565b60008251611d06818460208701611cc8565b9190910192915050565b60008219821115611d2357611d23611b96565b500190565b60008060408385031215611d3b57600080fd5b82519150602083015167ffffffffffffffff811115611d5957600080fd5b8301601f81018513611d6a57600080fd5b8051611d7861191382611852565b818152866020838501011115611d8d57600080fd5b611d9e826020830160208601611cc8565b8093505050509250929050565b600081611dba57611dba611b96565b506000190190565b6020815260008251806020840152611de1816040850160208701611cc8565b601f01601f19169190910160400192915050565b600082611e0457611e04611bcb565b50069056fe5461736b4578656375746f723a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564041334f809138adff4aed76ee4e45b3671e485ee2dcac112682c24d3a0c217365461736b4578656375746f723a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65641af59a3fd3f5a4bba6259b5a65dd4f4fbaab48545aeeabdfb60969120dbd5c35a2646970667358221220772eacd325f7ddf9f851a0e04085d3cdcf9a274d3e2eda04cea71d91225b353064736f6c634300080a003300000000000000000000000064585922a9703d9ede7d353a6522eb2970f7506600000000000000000000000024fdb881efaaf200c29c7449fb16845cc081bab7

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

00000000000000000000000064585922a9703d9ede7d353a6522eb2970f7506600000000000000000000000024fdb881efaaf200c29c7449fb16845cc081bab7

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

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000064585922a9703d9ede7d353a6522eb2970f75066
Arg [1] : 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.