ETH Price: $3,842.36 (-3.63%)

Contract

0x34fe09aF4f91AB2B37451E4680Fb528DFe12eF85
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Latest 1 internal transaction

Parent Transaction Hash Block From To
64888202024-07-24 2:17:35138 days ago1721787455  Contract Creation0 ETH

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LoopooorModuleD

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 200000 runs

Other Settings:
paris EvmVersion
File 1 of 18 : LoopooorModuleD.sol
// SPDX-License-Identifier: none
pragma solidity 0.8.24;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { Blastable } from "./../utils/Blastable.sol";
import { BlastableLibrary } from "./../libraries/BlastableLibrary.sol";
import { Calls } from "./../libraries/Calls.sol";
import { Errors } from "./../libraries/Errors.sol";
import { ILoopooorModuleD } from "./../interfaces/modules/ILoopooorModuleD.sol";
import { IRateContract } from "./../interfaces/external/Duo/IRateContract.sol";
import { IWrapMintV2 } from "./../interfaces/external/Duo/IWrapMintV2.sol";
import { IOErc20Delegator } from "./../interfaces/external/Orbit/IOErc20Delegator.sol";
import { IPriceOracle } from "./../interfaces/external/Orbit/IPriceOracle.sol";
import { IOrbitSpaceStationV4 } from "./../interfaces/external/Orbit/IOrbitSpaceStationV4.sol";
import { IWETH } from "./../interfaces/external/tokens/IWETH.sol";

/**
 * @title LoopooorModuleD
 * @author AgentFi
 * @notice A module used in the Loopooor strategy.
 *
 * Designed for use on Blast Mainnet only.
 */

contract LoopooorModuleD is Blastable, ILoopooorModuleD {
    /***************************************
    CONSTANTS
    ***************************************/

    uint256 internal constant PRECISION_CF = 10 ** 18; // Precision of collatral factor from orbit
    uint256 internal constant PRECISION_LEVERAGE = 10 ** 18; // Precision of leverage input (10 ** 18 = 1x leverage)
    uint256 internal constant PRECISION_EXCHANGE_RATE = 10 ** 18; // Precision of exchange rate between oToken and duo asset
    uint256 internal constant PRECISION_PRICE = 10 ** 18; // Precision of price of duo asset

    address internal constant _eth = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address internal constant _weth = 0x4300000000000000000000000000000000000004; // wrapped eth

    /***************************************
    STATE
    ***************************************/
    bytes32 private constant LOOPOOR_MODULED_STORAGE_POSITION = keccak256("agentfi.storage.loopoormoduleD");

    struct LoopooorModuleDStorage {
        address oToken;
        address rateContract; // Fixed or variable storage contract
        address underlying;
        address wrapMint;
        MODE mode;
    }

    function loopooorModuleDStorage() internal pure returns (LoopooorModuleDStorage storage s) {
        bytes32 position_ = LOOPOOR_MODULED_STORAGE_POSITION;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            s.slot := position_
        }
    }

    /***************************************
    CONSTRUCTOR
    ***************************************/

    /**
     * @notice Constructs the LoopooorModuleD contract.
     * @param blast_ The address of the blast gas reward contract.
     * @param gasCollector_ The address of the gas collector.
     * @param blastPoints_ The address of the blast points contract.
     * @param pointsOperator_ The address of the blast points operator.
     */
    constructor(
        address blast_,
        address gasCollector_,
        address blastPoints_,
        address pointsOperator_
    ) Blastable(blast_, gasCollector_, blastPoints_, pointsOperator_) {}

    /***************************************
    VIEW FUNCTIONS
    ***************************************/

    function moduleName() external pure override returns (string memory name_) {
        name_ = "LoopooorModuleD";
    }

    function strategyType() external pure override returns (string memory type_) {
        type_ = "Loopooor";
    }

    function eth() external pure override returns (address) {
        return _eth;
    }

    function weth() external pure override returns (address) {
        return _weth;
    }

    function mode() public view override returns (MODE) {
        return loopooorModuleDStorage().mode;
    }

    function rateContract() public view override returns (address) {
        return loopooorModuleDStorage().rateContract;
    }

    function underlying() public view override returns (address) {
        return loopooorModuleDStorage().underlying;
    }

    function wrapMint() public view override returns (address) {
        return loopooorModuleDStorage().wrapMint;
    }

    function oToken() public view override returns (IOErc20Delegator) {
        return IOErc20Delegator(loopooorModuleDStorage().oToken);
    }

    function comptroller() public view override returns (IOrbitSpaceStationV4) {
        return getComptroller(address(oToken()));
    }

    function duoAsset() public view override returns (IERC20) {
        return getDuoAssetFromOToken(address(oToken()));
    }

    function leverage() public view override returns (uint256) {
        uint256 supply = supplyBalance();
        uint256 borrow = borrowBalance();
        return Math.mulDiv(supply, PRECISION_LEVERAGE, supply - borrow);
    }

    function supplyBalance() public view override returns (uint256 supply_) {
        IOErc20Delegator oToken_ = oToken();
        if (address(oToken_) == address(0)) {
            return 0;
        }

        uint256 exchangeRate = oToken_.exchangeRateStored();

        supply_ = oToken_.balanceOf(address(this));
        supply_ = Math.mulDiv(supply_, exchangeRate, PRECISION_EXCHANGE_RATE);
    }

    function borrowBalance() public view override returns (uint256 borrow_) {
        IOErc20Delegator oToken_ = oToken();
        if (address(oToken_) == address(0)) {
            return 0;
        }

        borrow_ = oToken_.borrowBalanceStored(address(this));
    }

    function getComptroller(address oToken_) internal view returns (IOrbitSpaceStationV4) {
        if (oToken_ == address(0)) {
            return IOrbitSpaceStationV4(address(0));
        }
        return IOrbitSpaceStationV4(IOErc20Delegator(oToken_).comptroller());
    }

    function getDuoAssetFromOToken(address oToken_) internal view returns (IERC20) {
        if (oToken_ == address(0)) {
            return IERC20(address(0));
        }
        return IERC20(IOErc20Delegator(oToken_).underlying());
    }

    function getDuoAssetFromWrapMint(address wrapMint_) internal view returns (IERC20) {
        if (wrapMint_ == address(0)) {
            return IERC20(address(0));
        }
        return IERC20(IWrapMintV2(wrapMint_).duoAssetToken());
    }

    function _quoteClaimWithRevert() external {
        moduleD_claim();
        uint256 balance = IERC20(comptroller().getTokenAddress()).balanceOf(address(this));
        revert Errors.RevertForAmount(balance);
    }

    function quoteClaim() external returns (uint256 balance_) {
        try LoopooorModuleD(payable(address(this)))._quoteClaimWithRevert() {} catch (bytes memory reason) {
            balance_ = BlastableLibrary.parseRevertReasonForAmount(reason);
        }
    }

    function _quoteBalanceWithRevert() external {
        uint256 balance = moduleD_withdrawBalance();
        revert Errors.RevertForAmount(balance);
    }

    /**
     * @notice Returns the balance in underlying asset of the contract.
     * @dev Should be a view function, but requires on state change and revert
     */
    function quoteBalance() external returns (uint256 balance) {
        try LoopooorModuleD(payable(address(this)))._quoteBalanceWithRevert() {} catch (bytes memory reason) {
            balance = BlastableLibrary.parseRevertReasonForAmount(reason);
        }
    }

    /***************************************
    LOW LEVEL DUO MUTATOR FUNCTIONS
    ***************************************/
    function moduleD_mintFixedRate(
        address wrapMint_,
        address exchange,
        address token,
        uint256 amountIn,
        uint256 amountOutMin,
        uint256 minLockedYield,
        bytes memory data
    ) public payable override returns (address fixedRateContract_, uint256 amountOut, uint256 lockedYield) {
        IWrapMintV2 wrapper = IWrapMintV2(wrapMint_);

        _checkApproval(token, address(wrapper), amountIn);
        (fixedRateContract_, amountOut, lockedYield) = wrapper.mintFixedRate(
            exchange,
            token,
            amountIn,
            amountOutMin,
            minLockedYield,
            data
        );
    }

    function moduleD_mintFixedRateEth(
        address wrapMint_,
        address exchange,
        uint256 amountIn,
        uint256 amountOutMin,
        uint256 minLockedYield,
        bytes calldata data
    ) public payable override returns (address fixedRateContract_, uint256 amountOut, uint256 lockedYield) {
        IWrapMintV2 wrapper = IWrapMintV2(wrapMint_);

        (fixedRateContract_, amountOut, lockedYield) = wrapper.mintFixedRateEth{ value: amountIn }(
            exchange,
            amountIn,
            amountOutMin,
            minLockedYield,
            data
        );
    }

    function moduleD_mintVariableRate(
        address wrapMint_,
        address exchange,
        address token,
        uint256 amountIn,
        uint256 amountOutMin,
        bytes memory data
    ) public payable override returns (address variableRateContract_, uint256 amountOut) {
        IWrapMintV2 wrapper = IWrapMintV2(wrapMint_);

        _checkApproval(token, address(wrapper), amountIn);
        (variableRateContract_, amountOut) = wrapper.mintVariableRate(exchange, token, amountIn, amountOutMin, data);
    }

    function moduleD_mintVariableRateEth(
        address wrapMint_,
        address exchange,
        uint256 amountIn,
        uint256 amountOutMin,
        bytes memory data
    ) public payable override returns (address variableRateContract_, uint256 amountOut) {
        IWrapMintV2 wrapper = IWrapMintV2(wrapMint_);

        (variableRateContract_, amountOut) = wrapper.mintVariableRateEth{ value: amountIn }(
            exchange,
            amountIn,
            amountOutMin,
            data
        );
    }

    function moduleD_burnVariableRate(
        address wrapMint_,
        address variableRate,
        uint256 amount,
        uint256 minYield
    ) public payable override returns (uint256 yieldToUnlock, uint256 yieldToRelease) {
        _checkApproval(address(getDuoAssetFromWrapMint(wrapMint_)), wrapMint_, amount);

        (yieldToUnlock, yieldToRelease) = IWrapMintV2(wrapMint_).burnVariableRate(variableRate, amount, minYield);
    }

    function moduleD_burnFixedRate(
        address wrapMint_,
        address fixedRate,
        uint256 amount
    ) public payable override returns (uint256 yieldToUnlock, uint256 yieldToRelease) {
        _checkApproval(address(getDuoAssetFromWrapMint(wrapMint_)), wrapMint_, amount);
        (yieldToUnlock, yieldToRelease) = IWrapMintV2(wrapMint_).burnFixedRate(fixedRate, amount);
    }

    /***************************************
    LOW LEVEL ORBITER MUTATOR FUNCTIONS
    ***************************************/
    function moduleD_borrow(address oToken_, uint borrowAmount) public payable override returns (uint) {
        return IOErc20Delegator(oToken_).borrow(borrowAmount);
    }

    function moduleD_mint(address oToken_, uint mintAmount) public payable override returns (uint) {
        _checkApproval(address(getDuoAssetFromOToken(oToken_)), oToken_, mintAmount);
        return IOErc20Delegator(oToken_).mint(mintAmount);
    }

    function moduleD_repayBorrow(address oToken_, uint repayAmount) public payable override returns (uint) {
        _checkApproval(address(getDuoAssetFromOToken(oToken_)), oToken_, repayAmount);

        return IOErc20Delegator(oToken_).repayBorrow(repayAmount);
    }

    function moduleD_redeem(address oToken_, uint redeemTokens) public payable override returns (uint) {
        _checkApproval(oToken_, oToken_, redeemTokens);

        return IOErc20Delegator(oToken_).redeem(redeemTokens);
    }

    function moduleD_enterMarkets(
        address comptroller_,
        address[] memory oTokens
    ) public payable override returns (uint[] memory) {
        return IOrbitSpaceStationV4(comptroller_).enterMarkets(oTokens);
    }

    /***************************************
    HIGH LEVEL AGENT MUTATOR FUNCTIONS
    ***************************************/
    function moduleD_claim() internal {
        comptroller().claimOrb(address(this));
    }

    function moduleD_enterMarket() internal {
        address[] memory oTokens = new address[](1);
        oTokens[0] = address(oToken());
        moduleD_enterMarkets(address(comptroller()), oTokens);
    }

    function moduleD_depositBalance(
        address wrapMint_,
        address oToken_,
        address underlying_,
        MODE mode_,
        uint256 leverage_
    ) public payable override {
        LoopooorModuleDStorage storage state = loopooorModuleDStorage();

        if (state.rateContract != address(0)) {
            moduleD_withdrawBalance();
        }

        state.mode = mode_;
        state.oToken = oToken_;
        state.underlying = underlying_;
        state.wrapMint = wrapMint_;

        moduleD_enterMarket();

        if (underlying_ == _eth) {
            Calls.sendValue(_weth, address(this).balance);
            underlying_ = _weth;
        }

        uint256 balance = IERC20(underlying_).balanceOf(address(this));
        uint256 total = Math.mulDiv(balance, leverage_, PRECISION_LEVERAGE);

        if (mode_ == MODE.FIXED_RATE) {
            (address fixedRateContract_, , ) = moduleD_mintFixedRate(
                wrapMint_,
                address(0),
                underlying_,
                balance,
                0,
                0,
                new bytes(0)
            );
            state.rateContract = fixedRateContract_;
        }
        if (mode_ == MODE.VARIABLE_RATE) {
            (address variableRateContract_, ) = moduleD_mintVariableRate(
                wrapMint_,
                address(0),
                underlying_,
                balance,
                0,
                new bytes(0)
            );
            state.rateContract = variableRateContract_;
        }

        moduleD_mint(oToken_, balance);
        total -= balance;

        uint256 price = IPriceOracle(comptroller().oracle()).getUnderlyingPrice(address(oToken()));
        while (total > 0) {
            // Get maximum USD we can borrow
            (, uint256 liquidity, ) = comptroller().getAccountLiquidity(address(this));

            // Convert this to underlying
            liquidity = Math.mulDiv(liquidity, PRECISION_PRICE, price, Math.Rounding.Floor);

            // Borrow, and re-supply
            moduleD_borrow(oToken_, Math.min(total, liquidity));
            balance = duoAsset().balanceOf(address(this));
            moduleD_mint(oToken_, balance);

            total -= balance;
        }
    }

    function moduleD_withdrawBalance() public payable override returns (uint256 amount_) {
        LoopooorModuleDStorage storage state = loopooorModuleDStorage();

        IOErc20Delegator oToken_ = IOErc20Delegator(state.oToken);
        IERC20 duoAsset_ = duoAsset();
        IOrbitSpaceStationV4 comptroller_ = comptroller();

        uint256 exchangeRate = oToken_.exchangeRateCurrent();
        uint256 price = IPriceOracle(comptroller_.oracle()).getUnderlyingPrice(address(oToken()));
        (, uint256 collateralFactorMantissa, ) = comptroller_.markets(address(oToken()));

        // While i have borrow, withdrawal maximum collateral and repay
        uint256 borrow = oToken_.borrowBalanceCurrent(address(this));
        while (borrow > 0) {
            // Get maximum USD we can borrow
            (, uint256 liquidity, ) = comptroller_.getAccountLiquidity(address(this));

            // Get USD collateral we can withdraw
            liquidity = Math.mulDiv(liquidity, PRECISION_CF, collateralFactorMantissa, Math.Rounding.Floor);

            // Convert this to underlying
            liquidity = Math.mulDiv(liquidity, PRECISION_PRICE, price, Math.Rounding.Floor);

            // Covert underlying amount to oToken
            liquidity = Math.mulDiv(liquidity, PRECISION_EXCHANGE_RATE, exchangeRate, Math.Rounding.Floor);

            moduleD_redeem(state.oToken, liquidity);

            // Repay borrow
            uint256 balance = duoAsset_.balanceOf(address(this));
            moduleD_repayBorrow(state.oToken, Math.min(balance, borrow));

            borrow = oToken_.borrowBalanceCurrent(address(this));
        }

        // Final withdrawal
        moduleD_redeem(state.oToken, oToken_.balanceOf(address(this)));

        // Burn
        if (state.rateContract != address(0)) {
            uint256 burnAmount = Math.min(
                duoAsset_.balanceOf(address(this)),
                IRateContract(state.rateContract).principal()
            );
            if (state.mode == MODE.FIXED_RATE) {
                moduleD_burnFixedRate(state.wrapMint, state.rateContract, burnAmount);
            }
            if (state.mode == MODE.VARIABLE_RATE) {
                moduleD_burnVariableRate(state.wrapMint, state.rateContract, burnAmount, 0);
            }
            state.rateContract = address(0);
        }

        // Unwrap if necesary
        if (underlying() == _eth) {
            amount_ = IERC20(_weth).balanceOf(address(this));
            IWETH(_weth).withdraw(amount_);
        } else {
            amount_ = IERC20(state.underlying).balanceOf(address(this));
        }

        // claim orbit token
        moduleD_claim();
    }

    function moduleD_withdrawBalanceTo(address receiver) external payable override {
        moduleD_withdrawBalance();
        moduleD_sendBalanceTo(receiver, underlying());
        // Send any orbit.
        moduleD_sendBalanceTo(receiver, comptroller().getTokenAddress());
    }

    // Send funds to receiver
    function moduleD_sendBalanceTo(address receiver, address token) public payable override {
        if (token == _eth) {
            Calls.sendValue(receiver, address(this).balance);
        } else {
            SafeERC20.safeTransfer(IERC20(token), receiver, IERC20(token).balanceOf(address(this)));
        }
    }

    function moduleD_claimTo(address receiver) public {
        // Claim all orbs first
        moduleD_claim();

        // Send balance. Note in the event comptroller is empty, might not get full claim
        moduleD_sendBalanceTo(receiver, comptroller().getTokenAddress());
    }

    function moduleD_sendAmountTo(address receiver, address token, uint256 amount) public payable override {
        if (token == _eth) {
            Calls.sendValue(receiver, amount);
        } else {
            SafeERC20.safeTransfer(IERC20(token), receiver, amount);
        }
    }

    function moduleD_increaseWithBalance() public payable override {
        LoopooorModuleDStorage storage state = loopooorModuleDStorage();
        uint256 leverage_ = leverage();

        moduleD_withdrawBalance();

        moduleD_depositBalance(state.wrapMint, state.oToken, state.underlying, state.mode, leverage_);
    }

    function moduleD_partialWithdrawTo(address receiver, uint256 amount) external {
        LoopooorModuleDStorage storage state = loopooorModuleDStorage();
        uint256 leverage_ = leverage();

        moduleD_withdrawBalance();

        moduleD_sendAmountTo(receiver, state.underlying, amount);

        moduleD_depositBalance(state.wrapMint, state.oToken, state.underlying, state.mode, leverage_);
    }

    /***************************************
    HELPER FUNCTIONS
    ***************************************/

    /**
     * @notice Checks the approval of an ERC20 token from this contract to another address.
     * @param token The token to check allowance.
     * @param recipient The address to give allowance to.
     * @param minAmount The minimum amount of the allowance.
     */
    function _checkApproval(address token, address recipient, uint256 minAmount) internal {
        // if current allowance is insufficient
        if (IERC20(token).allowance(address(this), recipient) < minAmount) {
            // set allowance to max
            SafeERC20.forceApprove(IERC20(token), recipient, type(uint256).max);
        }
    }
}

File 2 of 18 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 3 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

File 4 of 18 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

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

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

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

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

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

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

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

File 5 of 18 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

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

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

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

File 6 of 18 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 7 of 18 : IRateContract.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/// @notice Shared interface for both fixed rate and variable rate contracts.
interface IRateContract {
    function principal() external view returns (uint256);
}

File 8 of 18 : IWrapMintV2.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IWrapMintV2 {
    /* Events */
    event MintFixedRate(address indexed fixedRate, address indexed owner, uint256 principal, uint256 yield);
    event BurnFixedRate(address indexed fixedRate, uint256 principal, uint256 yieldToUnlock, uint256 yieldToRelease);
    event MintVariableRate(address indexed variableRate, address indexed owner, uint256 amount);
    event BurnVariableRate(address indexed variableRate, uint256 amount, uint256 yield, uint256 fee);
    event UpdateExchange(address indexed exchange, bool status);
    event UpdateFixedRateNft(address indexed nft);
    event UpdateVariableRateNft(address indexed nft);
    event UpdateDuoAssetToken(address indexed duoAssetToken);

    function duoAssetToken() external view returns (address);
    function TOKEN() external view returns (address);

    /** @notice mint a fixed rate contract (represented as NFT), input with ERC20 token */
    function mintFixedRate(
        address exchange,
        address token,
        uint256 amountIn,
        uint256 amountOutMin,
        uint256 minLockedYield,
        bytes calldata data
    ) external returns (address fixedRateContract, uint256 amountOut, uint256 lockedYield);

    /** @notice mint a fixed rate contract (represented as NFT), input with ETH */
    function mintFixedRateEth(
        address exchange,
        uint256 amountIn,
        uint256 amountOutMin,
        uint256 minLockedYield,
        bytes calldata data
    ) external payable returns (address fixedRateContract, uint256 amountOut, uint256 lockedYield);

    /** @notice mint a variable rate contract, input with ETH */
    function mintVariableRateEth(
        address exchange,
        uint256 amountIn,
        uint256 amountOutMin,
        bytes calldata data
    ) external payable returns (address variableRateContract, uint256 amountOut);

    /**
     * @notice mint a variable rate contract, input with ERC20 token
     */
    function mintVariableRate(
        address exchange,
        address token,
        uint256 amountIn,
        uint256 amountOutMin,
        bytes calldata data
    ) external returns (address variableRateContract, uint256 amountOut);

    /**
     * @notice burn a variable rate contract, together with asset token, receiving principal and yield
     * @param variableRate the variable rate contract to burn
     * @param amount the amount of variable rate contract to burn
     * @param minYield the minimum amount of yield to unlock
     * @return yield the amount of yield unlocked
     * @return fee the amount of fee
     */
    function burnVariableRate(
        address variableRate,
        uint256 amount,
        uint256 minYield
    ) external returns (uint256 yield, uint256 fee);

    /**
     * @notice burn a fixed rate contract, together with asset token, receiving principal and yield
     * @param fixedRate the fixed rate contract to burn
     * @param amount the amount of fixed rate contract to burn
     * @return yieldToUnlock the amount of yield to unlock
     * @return yieldToRelease the amount of yield to release
     */
    function burnFixedRate(
        address fixedRate,
        uint256 amount
    ) external returns (uint256 yieldToUnlock, uint256 yieldToRelease);
}

File 9 of 18 : IOErc20Delegator.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IOErc20Delegator {
    // View
    function balanceOf(address account) external view returns (uint256);
    function comptroller() external view returns (address);
    function underlying() external view returns (address);

    /**
     * @notice Sender borrows assets from the protocol to their own address
     * @param borrowAmount The amount of the underlying asset to borrow
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function borrow(uint borrowAmount) external returns (uint);

    /**
     * @notice Sender supplies assets into the market and receives oTokens in exchange
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param mintAmount The amount of the underlying asset to supply
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function mint(uint mintAmount) external returns (uint);

    /**
     * @notice Sender repays their own borrow
     * @param repayAmount The amount to repay, or -1 for the full outstanding amount
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function repayBorrow(uint repayAmount) external returns (uint);

    /**
     * @notice Sender redeems oTokens in exchange for the underlying asset
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param redeemTokens The number of oTokens to redeem into underlying
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function redeem(uint redeemTokens) external returns (uint);

    /**
     * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex
     * @param account The address whose balance should be calculated after updating borrowIndex
     * @return The calculated balance
     */
    function borrowBalanceCurrent(address account) external returns (uint);

    /**
     * @notice Return the borrow balance of account based on stored data
     * @param account The address whose balance should be calculated
     * @return The calculated balance
     */
    function borrowBalanceStored(address account) external view returns (uint);

    /**
     * @notice Calculates the exchange rate from the underlying to the OToken
     * @dev This function does not accrue interest before calculating the exchange rate
     * @return Calculated exchange rate scaled by 1e18
     */
    function exchangeRateStored() external view returns (uint);

    /**
     * @notice Accrue interest then return the up-to-date exchange rate
     * @return Calculated exchange rate scaled by 1e18
     */
    function exchangeRateCurrent() external returns (uint);
}

File 10 of 18 : IOrbitSpaceStationV4.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IOrbitSpaceStationV4 {
    /**
     * @notice Returns whether the given account is entered in the given asset
     * @param account The address of the account to check
     * @param oToken The oToken to check
     * @return True if the account is in the asset, otherwise false.
     */
    function checkMembership(address account, address oToken) external view returns (bool);

    /**
     * @notice Add assets to be included in account liquidity calculation
     * @param oTokens The list of addresses of the oToken markets to be enabled
     * @return Success indicator for whether each corresponding market was entered
     */
    function enterMarkets(address[] memory oTokens) external returns (uint[] memory);

    /**
     * @notice Determine the current account liquidity wrt collateral requirements
     * @return (possible error code (semi-opaque),
                account liquidity in excess of collateral requirements,
     *          account shortfall below collateral requirements)
     */
    function getAccountLiquidity(address account) external view returns (uint, uint, uint);

    function oracle() external view returns (address);

    //isListed, collateralFactorMantissa, isComped
    function markets(address oTokenAddress) external view returns (bool, uint, bool);

    /**
     * @notice Claim all the incentive tokens accrued by holder in all markets
     * @param holder The address to claim tokens for
     */
    function claimOrb(address holder) external;

    /**
     * @notice Return the address of the tokens token
     * @return The address of tokens
     */
    function getTokenAddress() external view returns (address);
}

File 11 of 18 : IPriceOracle.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

abstract contract IPriceOracle {
    /// @notice Indicator that this is a PriceOracle contract (for inspection)
    bool public constant isPriceOracle = true;

    /**
     * @notice Get the underlying price of a cToken asset
     * @param oToken The cToken to get the underlying price of
     * @return The underlying asset price mantissa (scaled by 1e18).
     *  Zero means the price is unavailable.
     */
    function getUnderlyingPrice(address oToken) external view virtual returns (uint);
}

File 12 of 18 : IWETH.sol
// SPDX-License-Identifier: none
pragma solidity 0.8.24;

interface IWETH {
    function deposit() external payable;

    function transferFrom(address src, address dst, uint wad) external returns (bool);

    function withdraw(uint wad) external;
}

File 13 of 18 : ILoopooorModuleD.sol
// SPDX-License-Identifier: none
pragma solidity 0.8.24;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IOErc20Delegator } from "./../external/Orbit/IOErc20Delegator.sol";
import { IOrbitSpaceStationV4 } from "./../external/Orbit/IOrbitSpaceStationV4.sol";
import { IWrapMintV2 } from "./../external/Duo/IWrapMintV2.sol";

/**
 * @title ILoopooorModuleD
 * @author AgentFi
 * @notice Interface for the LoopooorModuleD contract.
 */
interface ILoopooorModuleD {
    /***************************************
    ENUMS
    ***************************************/

    enum MODE {
        DIRECT, // Direct 
        FIXED_RATE, // Mint Fixed Rate
        VARIABLE_RATE // Mint Variable Rate
    }

    /***************************************
    VIEW FUNCTIONS
    ***************************************/

    /**
     * @notice Returns the name of the module.
     * @return name_ The name of the module.
     */
    function moduleName() external pure returns (string memory name_);

    /**
     * @notice Returns the type of the strategy.
     * @return type_ The type of the strategy.
     */
    function strategyType() external pure returns (string memory type_);

    /**
     * @notice Returns the Ethereum address.
     * @return The Ethereum address.
     */
    function eth() external pure returns (address);

    /**
     * @notice Returns the Wrapped Ethereum address.
     * @return The Wrapped Ethereum address.
     */
    function weth() external pure returns (address);

    /**
     * @notice Returns the current mode of the contract.
     * @return The current mode of the contract.
     */
    function mode() external view returns (MODE);

    /**
     * @notice Returns the address of the rate contract.
     * @return The address of the rate contract.
     */
    function rateContract() external view returns (address);

    /**
     * @notice Returns the address of the underlying asset.
     * @return The address of the underlying asset.
     */
    function underlying() external view returns (address);

    /**
     * @notice Returns the address of the WrapMint contract.
     * @return The address of the WrapMint contract.
     */
    function wrapMint() external view returns (address);

    /**
     * @notice Returns the oToken contract.
     * @return The oToken contract.
     */
    function oToken() external view returns (IOErc20Delegator);

    /**
     * @notice Returns the Orbit comptroller contract.
     * @return The Orbit comptroller contract.
     */
    function comptroller() external view returns (IOrbitSpaceStationV4);

    /**
     * @notice Returns the supply balance of the contract.
     * @return supply_ The supply balance of the contract.
     */
    function supplyBalance() external view returns (uint256 supply_);

    /**
     * @notice Returns the borrow balance of the contract.
     * @return borrow_ The borrow balance of the contract.
     */
    function borrowBalance() external view returns (uint256 borrow_);

    /**
     * @notice Returns the address of the Duo asset.
     * @return The address of the Duo asset.
     */
    function duoAsset() external view returns (IERC20);

    /**
     * @notice The current scaled leverage of the position
     */
    function leverage() external view returns (uint256);

    /**
     * @notice Returns orbit, both in tba and unclaimed in the contract.
     * @dev Should be a view function, but requires on state change and revert
     */
    function quoteClaim() external returns (uint256 balance_);
    /**
     * @notice Returns the balance in underlying asset of the contract.
     * @dev Should be a view function, but requires on state change and revert
     */
    function quoteBalance() external returns (uint256 balance_);
    /***************************************
    LOW LEVEL DUO MUTATOR FUNCTIONS
    ***************************************/

    /**
     * @notice Mints a fixed rate position using the WrapMint contract.
     * @param wrapMint The address of the wrap contract.
     * @param exchange The address of the exchange contract.
     * @param token The address of the token to be used.
     * @param amountIn The amount of tokens to be deposited.
     * @param amountOutMin The minimum amount of tokens to be received.
     * @param minLockedYield The minimum locked yield.
     * @param data Additional data for the WrapMint contract.
     * @return fixedRateContract_ The address of the fixed rate contract.
     * @return amountOut The amount of tokens received.
     * @return lockedYield The locked yield.
     */
    function moduleD_mintFixedRate(
        address wrapMint,
        address exchange,
        address token,
        uint256 amountIn,
        uint256 amountOutMin,
        uint256 minLockedYield,
        bytes memory data
    ) external payable returns (address fixedRateContract_, uint256 amountOut, uint256 lockedYield);

    /**
     * @notice Mints a fixed rate position using the WrapMint contract with Ether.
     * @param wrapMint The address of the wrap contract.
     * @param exchange The address of the exchange contract.
     * @param amountIn The amount of Ether to be deposited.
     * @param amountOutMin The minimum amount of tokens to be received.
     * @param minLockedYield The minimum locked yield.
     * @param data Additional data for the WrapMint contract.
     * @return fixedRateContract_ The address of the fixed rate contract.
     * @return amountOut The amount of tokens received.
     * @return lockedYield The locked yield.
     */
    function moduleD_mintFixedRateEth(
        address wrapMint,
        address exchange,
        uint256 amountIn,
        uint256 amountOutMin,
        uint256 minLockedYield,
        bytes calldata data
    ) external payable returns (address fixedRateContract_, uint256 amountOut, uint256 lockedYield);

    /**
     * @notice Mints a variable rate position using the WrapMint contract.
     * @param wrapMint The address of the wrap contract.
     * @param exchange The address of the exchange contract.
     * @param token The address of the token to be used.
     * @param amountIn The amount of tokens to be deposited.
     * @param amountOutMin The minimum amount of tokens to be received.
     * @param data Additional data for the WrapMint contract.
     * @return variableRateContract_ The address of the variable rate contract.
     * @return amountOut The amount of tokens received.
     */
    function moduleD_mintVariableRate(
        address wrapMint,
        address exchange,
        address token,
        uint256 amountIn,
        uint256 amountOutMin,
        bytes memory data
    ) external payable returns (address variableRateContract_, uint256 amountOut);

    /**
     * @notice Mints a variable rate position using the WrapMint contract with Ether.
     * @param wrapMint The address of the wrap contract.
     * @param exchange The address of the exchange contract.
     * @param amountIn The amount of Ether to be deposited.
     * @param amountOutMin The minimum amount of tokens to be received.
     * @param data Additional data for the WrapMint contract.
     * @return variableRateContract_ The address of the variable rate contract.
     * @return amountOut The amount of tokens received.
     */
    function moduleD_mintVariableRateEth(
        address wrapMint,
        address exchange,
        uint256 amountIn,
        uint256 amountOutMin,
        bytes memory data
    ) external payable returns (address variableRateContract_, uint256 amountOut);

    /**
     * @notice Burns a variable rate position using the WrapMint contract.
     * @param wrapMint The address of the wrap contract.
     * @param variableRate The address of the variable rate contract.
     * @param amount The amount of tokens to be burned.
     * @param minYield The minimum yield to be received.
     * @return yieldToUnlock The amount of yield to be unlocked.
     * @return yieldToRelease The amount of yield to be released.
     */
    function moduleD_burnVariableRate(
        address wrapMint,
        address variableRate,
        uint256 amount,
        uint256 minYield
    ) external payable returns (uint256 yieldToUnlock, uint256 yieldToRelease);

    /**
     * @notice Burns a fixed rate position using the WrapMint contract.
     * @param wrapMint The address of the wrap contract.
     * @param fixedRate The address of the fixed rate contract.
     * @param amount The amount of tokens to be burned.
     * @return yieldToUnlock The amount of yield to be unlocked.
     * @return yieldToRelease The amount of yield to be released.
     */
    function moduleD_burnFixedRate(address wrapMint, address fixedRate, uint256 amount)
        external
        payable
        returns (uint256 yieldToUnlock, uint256 yieldToRelease);

    /***************************************
    LOW LEVEL ORBITER MUTATOR FUNCTIONS
    ***************************************/

    /**
     * @notice Borrows tokens from the Orbit protocol.
     * @param oToken The address of the oToken contract.
     * @param borrowAmount The amount of tokens to be borrowed.
     * @return The amount of tokens borrowed.
     */
    function moduleD_borrow(address oToken, uint256 borrowAmount) external payable returns (uint256);

    /**
     * @notice Mints tokens in the Orbit protocol.
     * @param oToken The address of the oToken contract.
     * @param mintAmount The amount of tokens to be minted.
     * @return The amount of tokens minted.
     */
    function moduleD_mint(address oToken, uint256 mintAmount) external payable returns (uint256);

    /**
     * @notice Repays a borrow in the Orbit protocol.
     * @param oToken The address of the oToken contract.
     * @param repayAmount The amount of tokens to be repaid.
     * @return The amount of tokens repaid.
     */
    function moduleD_repayBorrow(address oToken, uint256 repayAmount) external payable returns (uint256);

    /**
     * @notice Redeems tokens from the Orbit protocol.
     * @param oToken The address of the oToken contract.
     * @param redeemTokens The amount of tokens to be redeemed.
     * @return The amount of tokens redeemed.
     */
    function moduleD_redeem(address oToken, uint256 redeemTokens) external payable returns (uint256);

    /**
     * @notice Enters the specified markets in the Orbit protocol.
     * @param comptroller The address of the comptroller contract.
     * @param oTokens The addresses of the oTokens to enter.
     * @return The error codes for each market entered.
     */
    function moduleD_enterMarkets(address comptroller, address[] memory oTokens) external payable returns (uint256[] memory);

    /***************************************
    HIGH LEVEL AGENT MUTATOR FUNCTIONS
    ***************************************/

    /**
     * @notice Deposits the balance into the Orbit protocol and mints a fixed or variable rate position.
     * @param wrapMint_ The address of the WrapMint contract.
     * @param oToken_ The address of the oToken contract.
     * @param underlying_ The address of the underlying asset.
     * @param mode_ The mode to be used (fixed or variable rate).
     * @param leverage The leverage to be used.
     */
    function moduleD_depositBalance(
        address wrapMint_,
        address oToken_,
        address underlying_,
        MODE mode_,
        uint256 leverage
    ) external payable;

    /**
     * @notice Increases current position with any available balance.
     */
    function moduleD_increaseWithBalance() external payable; 

    /**
     * @notice Withdraws the balance from the Orbit protocol and burns the fixed or variable rate position.
     */
    function moduleD_withdrawBalance() external payable returns (uint256);

    /**
     * @notice Withdraws the balance from the Orbit protocol and burns the fixed or variable rate position, then sends the balance to the specified receiver.
     * @param receiver The address to send the balance to.
     */
    function moduleD_withdrawBalanceTo(address receiver) external payable;

    /**
     *  @notice Perform a partial withdrawal, sending amount to receiver
     */
    function moduleD_partialWithdrawTo(address receiver, uint256 amount) external;

    /**
     * @notice Sends the balance of the specified token to the specified receiver.
     * @param receiver The address to send the balance to.
     * @param token The address of the token to be sent.
     */
    function moduleD_sendBalanceTo(address receiver, address token) external payable;
    function moduleD_sendAmountTo(address receiver, address token, uint256 amount) external payable;
}

File 14 of 18 : IBlastable.sol
// SPDX-License-Identifier: none
pragma solidity 0.8.24;


/**
 * @title IBlastable
 * @author AgentFi
 * @notice An abstract contract that configures the connection to Blast during deployment
 *
 * This involves collecting ETH yield, gas rewards, and Blast Points. ETH yield is earned by this contract automatically, while gas rewards and Blast Points are delegated to dedicated collectors.
 */
interface IBlastable {

    /**
     * @notice Returns the address of the Blast contract.
     * @return blast_ The adress of the Blast contract.
     */
    function blast() external view returns (address blast_);

    /**
     * @notice Returns the address of the BlastPoints contract.
     * @return blastPoints_ The adress of the BlastPoints contract.
     */
    function blastPoints() external view returns (address blastPoints_);
}

File 15 of 18 : BlastableLibrary.sol
// SPDX-License-Identifier: none
pragma solidity 0.8.24;

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


/**
 * @title BlastableLibrary
 * @author AgentFi
 * @notice A library that helps contracts interact with Blast.
 */
library BlastableLibrary {

    /***************************************
    HELPER FUNCTIONS
    ***************************************/

    /**
     * @notice Parses a revert reason that should contain the numeric quote.
     * @param reason The error to parse.
     * @return amount The returned amount.
     */
    function parseRevertReasonForAmount(bytes memory reason) internal pure returns (uint256 amount) {
        // revert if reason is not of expected format
        if(reason.length != 36) {
            // look for revert reason and bubble it up if present
            if(reason.length > 0) {
                // the easiest way to bubble the revert reason is using memory via assembly
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let reason_size := mload(reason)
                    revert(add(32, reason), reason_size)
                }
            } else {
                revert Errors.UnknownError();
            }
        }
        // parse reason, return amount
        // solhint-disable-next-line no-inline-assembly
        assembly {
            reason := add(reason, 0x04)
        }
        amount = abi.decode(reason, (uint256));
    }
}

File 16 of 18 : Calls.sol
// SPDX-License-Identifier: none
pragma solidity 0.8.24;

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


/**
 * @title Calls
 * @author AgentFi
 * @notice A library for safely making low level calls.
 */
library Calls {

    /**
     * @notice Safely transfers the gas token using a low level `call`.
     * @dev If `target` reverts with a revert reason, it is bubbled up by this function.
     * @param target The address of the contract to `call`.
     * @return result The result of the function call.
     */
    function sendValue(
        address target,
        uint256 value
    ) internal returns (bytes memory result) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance();
        }
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{value:value}("");
        if(success) {
            result = 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
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert Errors.CallFailed();
            }
        }
    }

    /**
     * @notice Safely performs a Solidity function call using a low level `call`.
     * @dev If `target` reverts with a revert reason, it is bubbled up by this function.
     * @param target The address of the contract to `delegatecall`.
     * @param data The data to pass to the target.
     * @return result The result of the function call.
     */
    function functionCall(
        address target,
        bytes memory data
    ) internal returns (bytes memory result) {
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call(data);
        if(success) {
            result = 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
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert Errors.CallFailed();
            }
        }
    }

    /**
     * @notice Safely performs a Solidity function call using a low level `call`.
     * @dev If `target` reverts with a revert reason, it is bubbled up by this function.
     * @param target The address of the contract to `delegatecall`.
     * @param data The data to pass to the target.
     * @return result The result of the function call.
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory result) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance();
        }
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{value:value}(data);
        if(success) {
            result = 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
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert Errors.CallFailed();
            }
        }
    }

    /**
     * @notice Safely performs a Solidity function call using a low level `delegatecall`.
     * @dev If `target` reverts with a revert reason, it is bubbled up by this function.
     * @param target The address of the contract to `delegatecall`.
     * @param data The data to pass to the target.
     * @return result The result of the function call.
     */
    function functionDelegateCall(
        address target,
        bytes memory data
    ) internal returns (bytes memory result) {
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        if(success) {
            result = 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
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert Errors.DelegateCallFailed();
            }
        }
    }

    /**
     * @notice Verify that an address has contract code, otherwise reverts.
     * @param target The address to verify.
     */
    function verifyHasCode(
        address target
    ) internal view {
        // checks
        uint256 contractSize;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            contractSize := extcodesize(target)
        }
        if(contractSize == 0) revert Errors.NotAContract();
    }
}

File 17 of 18 : Errors.sol
// SPDX-License-Identifier: none
pragma solidity 0.8.24;


/**
 * @title Errors
 * @author AgentFi
 * @notice A library of custom error types used in BOOM!.
 */
library Errors {

    // call errors
    /// @notice Thrown when a low level call reverts without a reason.
    error CallFailed();
    /// @notice Thrown when a low level delegatecall reverts without a reason.
    error DelegateCallFailed();
    /// @notice Thrown if the owner tries to execute an operation that is not a call.
    error OnlyCallsAllowed();
    /// @notice Thrown when a function should not be delegatecalled.
    error NoDelegateCall();
    /// @notice Thrown when using an address with no code.
    error NotAContract();
    /// @notice Thrown when a contract deployment fails.
    error ContractNotDeployed();
    /// @notice Thrown when the sender has an insufficient balance of the token they are sending.
    error InsufficientBalance();

    // ownership & authentication errors
    /// @notice Thrown when calling a function reserved for the contract owner.
    error NotContractOwner();
    /// @notice Thrown when calling a function reserved for the pending contract owner.
    error NotPendingContractOwner();
    /// @notice Thrown when calling a function reserved for the owner of a erc6551 account.
    error ERC6551InvalidSigner();
    /// @notice Thrown when attempting a function reserved for the owner of the agent.
    error NotOwnerOfAgent();
    /// @notice Thrown when a signature is invalid.
    error InvalidSignature();

    // generic input errors
    /// @notice Thrown when address zero is used where it should not be.
    error AddressZero();
    /// @notice Thrown when a nonzero address is used where the zero address is expected
    error AddressNotZero();
    /// @notice Thrown when an address is used where it should not be.
    //error AddressIllegal();
    /// @notice Thrown when a zero amount used where it should not be.
    error AmountZero();
    /// @notice Thrown when the number of elements in an array is not what was expected.
    error LengthMismatch();
    /// @notice Thrown when receiving an array of length zero.
    error LengthZero();
    /// @notice Thrown when looking up a name that is unknown.
    error UnknownName();
    /// @notice Thrown when accessing an element that is out of range.
    error OutOfRange();
    /// @notice Thrown when gas token values do not match.
    error ValueMismatch();
    /// @notice Thrown when an entry has already been registered.
    error AlreadyRegistered();

    // execution errors
    /// @notice Thrown when a call reenters illegally.
    error ReentrancyGuard();
    /// @notice Thrown when attempting to initialize a contract that has already been initialized.
    error AlreadyInitialized();

    // nft errors
    /// @notice Thrown when querying an agent that does not exist.
    error AgentDoesNotExist();
    /// @notice Thrown when transferring an agent nft to the agent account.
    error OwnershipCycle();
    /// @notice Thrown when referrencing an account that is not an agent.
    error NotAnAgent();

    // agent creation errors
    /// @notice Thrown when attempting to create an agent from an account that is not whitelisted.
    error FactoryNotWhitelisted();
    /// @notice Thrown when call a contract that has been paused.
    error ContractPaused();
    /// @notice Thrown when using a factory and a creation settings that has been paused.
    error CreationSettingsPaused();
    /// @notice Thrown when minting an nft over the max total supply.
    error OverMaxSupply();
    /// @notice Thrown when minting an nft over the max public mint.
    error OverMaxPublicMint();
    /// @notice Thrown when minting an nft but the mint has not been started.
    error MintNotStarted();
    /// @notice Thrown when minting via the allowlist but the period has ended.
    error AllowlistMintEnded();
    /// @notice Thrown when minting too many agents at once.
    error OverMaxMintPerTx();
    /// @notice Thrown when minting an nft over the max allowlist mint total.
    error OverMaxAllowlistMintTotal();
    /// @notice Thrown when minting an nft over the max allowlist mint per user.
    error OverMaxAllowlistMintPerAccount();
    /// @notice Thrown when minting from the treasury allocation before treasury mint starts.
    error TreasuryMintNotStarted();
    /// @notice Thrown when not paying enough to mint an nft.
    error InsufficientPayment();
    /// @notice Thrown when minting from the treasury allocation without approval.
    error NotTreasuryMinter();
    /// @notice Thrown when minting more agents than allowed per user.
    error OverMaxCreationsPerUser();
    /// @notice Thrown when minting more agents than allowed per agent.
    error OverMaxCreationsPerAgent();

    // erc2535 errors
    /// @notice Thrown when installing a function that is already installed.
    error AddFunctionDuplicate();
    /// @notice Thrown when replacing a function with itself.
    error ReplaceFunctionSame();
    /// @notice Thrown when removing a function that has not currently installed.
    error RemoveFunctionDoesNotExist();
    /// @notice Thrown when removing a function that cannot be removed.
    error RemoveFunctionImmutable();
    /// @notice Thrown when calling a function that does not exist in this contract.
    error FunctionDoesNotExist();
    /// @notice Thrown when attempting to install a module that is not whitelisted.
    error ModuleNotWhitelisted();

    // quoter errors
    /// @notice Thrown when failing to decode an error message.
    error UnknownError();
    /// @notice Thrown when a revert was intentionally thrown in order to return a value.
    error RevertForAmount(uint256 amount);

    /// @notice Thrown when calling a function on a proxy that should only be called on the implementation.
    error NotImplementation();
    /// @notice Thrown when calling a function on an implementation contract that can only be called by the gas collector.
    error NotGasCollector();
    /// @notice Thrown when trying to mint without the minter role.
    error NotMinter();
    /// @notice Thrown when calling the dispatcher without the operator role.
    error NotOperator();

    // erc6551 errors
    error InvalidOperation();
    error ContractCreationFailed();
    error NotAuthorized();
    error InvalidInput();
    error ExceedsMaxLockTime();
    error AccountLocked();
    error InvalidAccountProof();
    error InvalidGuardian();
    error InvalidImplementation();
    //error AlreadyInitialized();
    error InvalidEntryPoint();
    error InvalidMulticallForwarder();
    error InvalidERC6551Registry();
    error InvalidSender();

    //ModuleC Errors
    error PositionAlreadyExists(); // Throw if trying to create a position, but one exists
    error NoPositionFound(); // Throw if trying to operate on a position, but none exists
    error InvalidTickParam(); // Throw if tick params is invalid
    error InvalidSlippageParam(); // Throw if slippage params is invalid
    error InvalidManagerParam(); // Throw if nfp manager param is invalid

    // ModuleD errors
    /// @notice Thrown when creating an agent with an invalid mode.
    error InvalidMode();

    // processing errors
    /// @notice Thrown when processing an empty queue.
    error NoMoreItemsInQueue();
}

File 18 of 18 : Blastable.sol
// SPDX-License-Identifier: none
pragma solidity 0.8.24;

import { IBlastable } from "./../interfaces/utils/IBlastable.sol";


/**
 * @title Blastable
 * @author AgentFi
 * @notice An abstract contract that configures the connection to Blast during deployment
 *
 * This involves collecting ETH yield, gas rewards, and Blast Points. ETH yield is earned by this contract automatically, while gas rewards and Blast Points are delegated to dedicated collectors.
 */
abstract contract Blastable is IBlastable {

    address internal immutable __blast;
    address internal immutable __gasCollector;
    address internal immutable __blastPoints;
    address internal immutable __pointsOperator;

    /**
     * @notice Constructs the Blastable contract.
     * Configures the contract to receive automatic yield, claimable gas, and assigns a gas collector.
     * @param blast_ The address of the blast gas reward contract.
     * @param gasCollector_ The address of the gas collector.
     * @param blastPoints_ The address of the blast points contract.
     * @param pointsOperator_ The address of the blast points operator.
     */
    constructor(
        address blast_,
        address gasCollector_,
        address blastPoints_,
        address pointsOperator_
    ) {
        __blast = blast_;
        __gasCollector = gasCollector_;
        __blastPoints = blastPoints_;
        __pointsOperator = pointsOperator_;
        // allow these calls to fail on local fork
        // check success after deployment
        blast_.call(abi.encodeWithSignature("configureAutomaticYield()"));
        blast_.call(abi.encodeWithSignature("configureClaimableGas()"));
        if(gasCollector_ != address(0)) blast_.call(abi.encodeWithSignature("configureGovernor(address)", gasCollector_));
        if(pointsOperator_ != address(0)) blastPoints_.call(abi.encodeWithSignature("configurePointsOperator(address)", pointsOperator_));
    }

    /**
     * @notice Returns the address of the Blast contract.
     * @return blast_ The adress of the Blast contract.
     */
    function blast() public view override returns (address blast_) {
        blast_ = __blast;
    }

    /**
     * @notice Returns the address of the BlastPoints contract.
     * @return blastPoints_ The adress of the BlastPoints contract.
     */
    function blastPoints() public view override returns (address blastPoints_) {
        blastPoints_ = __blastPoints;
    }

    /**
     * @notice Allows this contract to receive the gas token.
     */
    // solhint-disable-next-line no-empty-blocks
    receive() external payable virtual {}
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"blast_","type":"address"},{"internalType":"address","name":"gasCollector_","type":"address"},{"internalType":"address","name":"blastPoints_","type":"address"},{"internalType":"address","name":"pointsOperator_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"CallFailed","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RevertForAmount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"UnknownError","type":"error"},{"inputs":[],"name":"_quoteBalanceWithRevert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_quoteClaimWithRevert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blast","outputs":[{"internalType":"address","name":"blast_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blastPoints","outputs":[{"internalType":"address","name":"blastPoints_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowBalance","outputs":[{"internalType":"uint256","name":"borrow_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract IOrbitSpaceStationV4","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"duoAsset","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"eth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"leverage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mode","outputs":[{"internalType":"enum ILoopooorModuleD.MODE","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oToken_","type":"address"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"moduleD_borrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"wrapMint_","type":"address"},{"internalType":"address","name":"fixedRate","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"moduleD_burnFixedRate","outputs":[{"internalType":"uint256","name":"yieldToUnlock","type":"uint256"},{"internalType":"uint256","name":"yieldToRelease","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"wrapMint_","type":"address"},{"internalType":"address","name":"variableRate","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minYield","type":"uint256"}],"name":"moduleD_burnVariableRate","outputs":[{"internalType":"uint256","name":"yieldToUnlock","type":"uint256"},{"internalType":"uint256","name":"yieldToRelease","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"moduleD_claimTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wrapMint_","type":"address"},{"internalType":"address","name":"oToken_","type":"address"},{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"enum ILoopooorModuleD.MODE","name":"mode_","type":"uint8"},{"internalType":"uint256","name":"leverage_","type":"uint256"}],"name":"moduleD_depositBalance","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"comptroller_","type":"address"},{"internalType":"address[]","name":"oTokens","type":"address[]"}],"name":"moduleD_enterMarkets","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"moduleD_increaseWithBalance","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"oToken_","type":"address"},{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"moduleD_mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"wrapMint_","type":"address"},{"internalType":"address","name":"exchange","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"uint256","name":"minLockedYield","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"moduleD_mintFixedRate","outputs":[{"internalType":"address","name":"fixedRateContract_","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"lockedYield","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"wrapMint_","type":"address"},{"internalType":"address","name":"exchange","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"uint256","name":"minLockedYield","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"moduleD_mintFixedRateEth","outputs":[{"internalType":"address","name":"fixedRateContract_","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"lockedYield","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"wrapMint_","type":"address"},{"internalType":"address","name":"exchange","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"moduleD_mintVariableRate","outputs":[{"internalType":"address","name":"variableRateContract_","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"wrapMint_","type":"address"},{"internalType":"address","name":"exchange","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"moduleD_mintVariableRateEth","outputs":[{"internalType":"address","name":"variableRateContract_","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"moduleD_partialWithdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oToken_","type":"address"},{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"moduleD_redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"oToken_","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"moduleD_repayBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"moduleD_sendAmountTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"moduleD_sendBalanceTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"moduleD_withdrawBalance","outputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"moduleD_withdrawBalanceTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"moduleName","outputs":[{"internalType":"string","name":"name_","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"oToken","outputs":[{"internalType":"contract IOErc20Delegator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"quoteClaim","outputs":[{"internalType":"uint256","name":"balance_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rateContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyType","outputs":[{"internalType":"string","name":"type_","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"supplyBalance","outputs":[{"internalType":"uint256","name":"supply_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"wrapMint","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101006040523480156200001257600080fd5b506040516200432c3803806200432c8339810160408190526200003591620002df565b6001600160a01b03808516608081905281851660a05281841660c05290821660e05260408051600481526024810182526020810180516001600160e01b031663388a0bbd60e11b179052905186928692869286929162000095916200033c565b6000604051808303816000865af19150503d8060008114620000d4576040519150601f19603f3d011682016040523d82523d6000602084013e620000d9565b606091505b505060408051600481526024810182526020810180516001600160e01b0316634e606c4760e01b17905290516001600160a01b03871692506200011d91906200033c565b6000604051808303816000865af19150503d80600081146200015c576040519150601f19603f3d011682016040523d82523d6000602084013e62000161565b606091505b5050506001600160a01b038316156200020c576040516001600160a01b03848116602483015285169060440160408051601f198184030181529181526020820180516001600160e01b0316631d70c8d360e31b17905251620001c491906200033c565b6000604051808303816000865af19150503d806000811462000203576040519150601f19603f3d011682016040523d82523d6000602084013e62000208565b606091505b5050505b6001600160a01b03811615620002b4576040516001600160a01b03828116602483015283169060440160408051601f198184030181529181526020820180516001600160e01b03166336b91f2b60e01b179052516200026c91906200033c565b6000604051808303816000865af19150503d8060008114620002ab576040519150601f19603f3d011682016040523d82523d6000602084013e620002b0565b606091505b5050505b50505050505050506200036d565b80516001600160a01b0381168114620002da57600080fd5b919050565b60008060008060808587031215620002f657600080fd5b6200030185620002c2565b93506200031160208601620002c2565b92506200032160408601620002c2565b91506200033160608601620002c2565b905092959194509250565b6000825160005b818110156200035f576020818601810151858301520162000343565b506000920191825250919050565b60805160a05160c05160e051613f8b620003a16000396000505060006106da01526000505060006103480152613f8b6000f3fe6080604052600436106102a45760003560e01c806382ccd3301161016e578063beeb3261116100cb578063e2eefd3d1161007f578063edabb82d11610064578063edabb82d146107f1578063eee24219146107f9578063f1a2e8491461084357600080fd5b8063e2eefd3d146107c9578063e7b4e734146107dc57600080fd5b8063cd48069b116100b0578063cd48069b1461077f578063d00448ec14610794578063dc3be667146107b457600080fd5b8063beeb326114610757578063cbefbc261461076c57600080fd5b8063b2bd6b5011610122578063b627fbb911610107578063b627fbb91461071e578063bbdfbc4714610731578063bdaab1a61461074457600080fd5b8063b2bd6b50146106cb578063b440ae7b146106fe57600080fd5b80639080037311610153578063908003731461061b57806393f0899a14610665578063a6b204c1146106ab57600080fd5b806382ccd330146105a55780638c7c9e0c146105f457600080fd5b8063378e38bf1161021c578063559c47e1116101d05780636eb73f74116101b55780636eb73f74146105355780636f307dc314610548578063708322591461059257600080fd5b8063559c47e11461050d5780635fe3b5671461052057600080fd5b80633fc8cef3116102015780633fc8cef3146104be5780634808034e146104e55780634c711a2a146104f857600080fd5b8063378e38bf146104a15780633da8dddb146104b457600080fd5b8063243cd14e116102735780632c86d98e116102585780632c86d98e146104515780632fd3858c14610466578063345026821461048e57600080fd5b8063243cd14e146103d7578063295a5212146103fa57600080fd5b8063037581e6146102b0578063136bcd12146102fa578063175e1a7d146103395780631a32aad61461038d57600080fd5b366102ab57005b600080fd5b6102c36102be3660046134ed565b610858565b6040805173ffffffffffffffffffffffffffffffffffffffff90941684526020840192909252908201526060015b60405180910390f35b61030d61030836600461357f565b61091b565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152016102f1565b34801561034557600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102f1565b34801561039957600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff16610368565b3480156103e357600080fd5b506103ec6109d6565b6040519081526020016102f1565b34801561040657600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfab5474010000000000000000000000000000000000000000900460ff166040516102f19190613635565b34801561045d57600080fd5b506103ec610ad0565b610479610474366004613676565b610b0c565b604080519283526020830191909152016102f1565b6103ec61049c3660046136bc565b610bce565b6103ec6104af3660046136bc565b610c7e565b6104bc610cde565b005b3480156104ca57600080fd5b50734300000000000000000000000000000000000004610368565b6102c36104f33660046136e8565b610d67565b34801561050457600080fd5b506103ec610df7565b6104bc61051b36600461379d565b610e92565b34801561052c57600080fd5b50610368610eef565b6103ec6105433660046136bc565b610f3c565b34801561055457600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfaa5473ffffffffffffffffffffffffffffffffffffffff16610368565b61030d6105a03660046137de565b610f9d565b3480156105b157600080fd5b5060408051808201909152600881527f4c6f6f706f6f6f7200000000000000000000000000000000000000000000000060208201525b6040516102f191906138c2565b34801561060057600080fd5b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee610368565b34801561062757600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfab5473ffffffffffffffffffffffffffffffffffffffff16610368565b34801561067157600080fd5b5060408051808201909152600f81527f4c6f6f706f6f6f724d6f64756c6544000000000000000000000000000000000060208201526105e7565b3480156106b757600080fd5b506104bc6106c63660046136bc565b61104f565b3480156106d757600080fd5b507f0000000000000000000000000000000000000000000000000000000000000000610368565b34801561070a57600080fd5b506104bc6107193660046138d5565b6110fb565b6104bc61072c3660046138d5565b611180565b61047961073f36600461379d565b6111cb565b6103ec6107523660046136bc565b611285565b34801561076357600080fd5b506104bc6112db565b6104bc61077a3660046138f2565b611326565b34801561078b57600080fd5b506103ec611980565b6107a76107a236600461397e565b611afd565b6040516102f19190613a32565b3480156107c057600080fd5b506104bc611bb7565b6104bc6107d7366004613a76565b611cc7565b3480156107e857600080fd5b50610368611da9565b6103ec611df1565b34801561080557600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa95473ffffffffffffffffffffffffffffffffffffffff16610368565b34801561084f57600080fd5b506103ec612902565b600080808961086889828a61294c565b6040517fc27fe2a900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063c27fe2a9906108c4908d908d908d908d908d908d90600401613aaf565b6060604051808303816000875af11580156108e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109079190613b06565b919d909c50909a5098505050505050505050565b6000808761092a87828861294c565b6040517fb97012f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063b97012f790610984908b908b908b908b908b90600401613b3d565b60408051808303816000875af11580156109a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c69190613b8d565b909a909950975050505050505050565b600080610a177fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b905073ffffffffffffffffffffffffffffffffffffffff8116610a3c57600091505090565b6040517f95dd919300000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8216906395dd919390602401602060405180830381865afa158015610aa6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aca9190613bbb565b91505090565b600080610adb611980565b90506000610ae76109d6565b9050610b0582670de0b6b3a7640000610b008483613c03565b612a16565b9250505090565b600080610b22610b1b87612b12565b878661294c565b6040517f58752e1600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201869052604482018590528716906358752e169060640160408051808303816000875af1158015610b9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc19190613c16565b9097909650945050505050565b6000610be3610bdc84612ba6565b848461294c565b6040517f0e7527020000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff841690630e752702906024015b6020604051808303816000875af1158015610c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c759190613bbb565b90505b92915050565b6000610c8b83848461294c565b6040517fdb006a750000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff84169063db006a7590602401610c32565b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa86000610d09610ad0565b9050610d13611df1565b50600382015482546002840154610d639273ffffffffffffffffffffffffffffffffffffffff80821693811692169074010000000000000000000000000000000000000000900460ff1685611326565b5050565b6000806000808a90508073ffffffffffffffffffffffffffffffffffffffff16637eac08c78a8c8c8c8c8c8c6040518863ffffffff1660e01b8152600401610db496959493929190613c3a565b60606040518083038185885af1158015610dd2573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906109079190613b06565b60003073ffffffffffffffffffffffffffffffffffffffff1663dc3be6676040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e4157600080fd5b505af1925050508015610e52575060015b610e8f573d808015610e80576040519150601f19603f3d011682016040523d82523d6000602084013e610e85565b606091505b50610aca81612c16565b90565b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff831601610edf57610ed98382612c7d565b50505050565b610eea828483612d76565b505050565b6000610f37610f327fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b612df7565b905090565b6000610f4a610bdc84612ba6565b6040517fa0712d680000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff84169063a0712d6890602401610c32565b6040517fff6926ed0000000000000000000000000000000000000000000000000000000081526000908190879073ffffffffffffffffffffffffffffffffffffffff82169063ff6926ed908890610ffe908b9083908b908b90600401613cb9565b604080518083038185885af115801561101b573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906110409190613b8d565b90999098509650505050505050565b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa8600061107a610ad0565b9050611084611df1565b5060028201546110ac90859073ffffffffffffffffffffffffffffffffffffffff1685610e92565b600382015482546002840154610ed99273ffffffffffffffffffffffffffffffffffffffff80821693811692169074010000000000000000000000000000000000000000900460ff1685611326565b611103612e67565b61117d8161110f610eef565b73ffffffffffffffffffffffffffffffffffffffff166310fe9ae86040518163ffffffff1660e01b8152600401602060405180830381865afa158015611159573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d79190613cf4565b50565b611188611df1565b50611103816107d77fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfaa5473ffffffffffffffffffffffffffffffffffffffff1690565b6000806111e16111da86612b12565b868561294c565b6040517fe862ca9900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301526024820185905286169063e862ca999060440160408051808303816000875af1158015611255573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112799190613c16565b90969095509350505050565b6040517fc5ebeaec0000000000000000000000000000000000000000000000000000000081526004810182905260009073ffffffffffffffffffffffffffffffffffffffff84169063c5ebeaec90602401610c32565b60006112e5611df1565b9050806040517ff08ba1fc00000000000000000000000000000000000000000000000000000000815260040161131d91815260200190565b60405180910390fd5b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa9547fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa89073ffffffffffffffffffffffffffffffffffffffff161561138f5761138d611df1565b505b6003810180548491907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000008360028111156113e2576113e2613606565b0217905550805473ffffffffffffffffffffffffffffffffffffffff8087167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161783556002830180548783169084161790556003830180549189169190921617905561144f612eec565b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8516016114c3576114aa73430000000000000000000000000000000000000447612c7d565b5073430000000000000000000000000000000000000493505b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8616906370a0823190602401602060405180830381865afa158015611530573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115549190613bbb565b9050600061156b8285670de0b6b3a7640000612a16565b9050600185600281111561158157611581613606565b036115f657604080516000808252602082019092526115ab908a9083908a90879083908190610858565b50506001850180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055505b600285600281111561160a5761160a613606565b0361167c5760408051600080825260208201909252611632908a9083908a908790839061091b565b506001850180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055505b6116868783610f3c565b506116918282613c03565b9050600061169d610eef565b73ffffffffffffffffffffffffffffffffffffffff16637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170b9190613cf4565b73ffffffffffffffffffffffffffffffffffffffff1663fc57d4df6117647fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa1580156117cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f19190613bbb565b90505b8115611975576000611804610eef565b6040517f5ec88c7900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690635ec88c7990602401606060405180830381865afa158015611870573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118949190613d11565b509150506118ad81670de0b6b3a7640000846000612fa4565b90506118bd896107528584612ff5565b506118c6611da9565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611932573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119569190613bbb565b93506119628985610f3c565b5061196d8484613c03565b9250506117f4565b505050505050505050565b6000806119c17fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b905073ffffffffffffffffffffffffffffffffffffffff81166119e657600091505090565b60008173ffffffffffffffffffffffffffffffffffffffff1663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a579190613bbb565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611ac4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ae89190613bbb565b9250610b058382670de0b6b3a7640000612a16565b6040517fc299823800000000000000000000000000000000000000000000000000000000815260609073ffffffffffffffffffffffffffffffffffffffff84169063c299823890611b52908590600401613d3f565b6000604051808303816000875af1158015611b71573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610c759190810190613d8d565b611bbf612e67565b6000611bc9610eef565b73ffffffffffffffffffffffffffffffffffffffff166310fe9ae86040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c379190613cf4565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611ca3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e59190613bbb565b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff821601611d0e57610eea8247612c7d565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610d63908290849073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611d80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da49190613bbb565b612d76565b6000610f37611dec7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b612ba6565b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa880546000919073ffffffffffffffffffffffffffffffffffffffff1682611e37611da9565b90506000611e43610eef565b905060008373ffffffffffffffffffffffffffffffffffffffff1663bd6d894d6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611e94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb89190613bbb565b905060008273ffffffffffffffffffffffffffffffffffffffff16637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f2b9190613cf4565b73ffffffffffffffffffffffffffffffffffffffff1663fc57d4df611f847fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015611fed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120119190613bbb565b905060008373ffffffffffffffffffffffffffffffffffffffff16638e8f294b61206f7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401606060405180830381865afa1580156120d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120fc9190613e28565b506040517f17bfdfbc0000000000000000000000000000000000000000000000000000000081523060048201529092506000915073ffffffffffffffffffffffffffffffffffffffff8816906317bfdfbc906024016020604051808303816000875af1158015612170573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121949190613bbb565b90505b80156123ef576040517f5ec88c7900000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff871690635ec88c7990602401606060405180830381865afa15801561220a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061222e9190613d11565b5091505061224781670de0b6b3a7640000856000612fa4565b905061225e81670de0b6b3a7640000866000612fa4565b905061227581670de0b6b3a7640000876000612fa4565b895490915061229a9073ffffffffffffffffffffffffffffffffffffffff1682610c7e565b506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8916906370a0823190602401602060405180830381865afa158015612308573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061232c9190613bbb565b8a549091506123559073ffffffffffffffffffffffffffffffffffffffff1661049c8386612ff5565b506040517f17bfdfbc00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8a16906317bfdfbc906024016020604051808303816000875af11580156123c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e69190613bbb565b92505050612197565b87546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526124889173ffffffffffffffffffffffffffffffffffffffff90811691908a16906370a0823190602401602060405180830381865afa158015612464573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104af9190613bbb565b50600188015473ffffffffffffffffffffffffffffffffffffffff16156126d8576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906125d69073ffffffffffffffffffffffffffffffffffffffff8916906370a0823190602401602060405180830381865afa15801561251a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253e9190613bbb565b8a60010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ba5d30786040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d19190613bbb565b612ff5565b9050600160038a015474010000000000000000000000000000000000000000900460ff16600281111561260b5761260b613606565b0361264157600389015460018a015461263e9173ffffffffffffffffffffffffffffffffffffffff9081169116836111cb565b50505b600260038a015474010000000000000000000000000000000000000000900460ff16600281111561267457612674613606565b036126ac57600389015460018a01546126a99173ffffffffffffffffffffffffffffffffffffffff9081169116836000610b0c565b50505b506001880180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61272b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfaa5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1603612858576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152734300000000000000000000000000000000000004906370a0823190602401602060405180830381865afa1580156127ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127d29190613bbb565b6040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810182905290995073430000000000000000000000000000000000000490632e1a7d4d90602401600060405180830381600087803b15801561283b57600080fd5b505af115801561284f573d6000803e3d6000fd5b505050506128ef565b60028801546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa1580156128c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ec9190613bbb565b98505b6128f7612e67565b505050505050505090565b60003073ffffffffffffffffffffffffffffffffffffffff1663beeb32616040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e4157600080fd5b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015282919085169063dd62ed3e90604401602060405180830381865afa1580156129c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129e59190613bbb565b1015610eea57610eea83837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61300b565b6000838302817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8587098281108382030391505080600003612a6b57838281612a6157612a61613e64565b0492505050612b0b565b808411612aa4576040517f227bc15300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150505b9392505050565b600073ffffffffffffffffffffffffffffffffffffffff8216612b3757506000919050565b8173ffffffffffffffffffffffffffffffffffffffff1663d7a433aa6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c789190613cf4565b600073ffffffffffffffffffffffffffffffffffffffff8216612bcb57506000919050565b8173ffffffffffffffffffffffffffffffffffffffff16636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b82573d6000803e3d6000fd5b60008151602414612c6357815115612c315781518083602001fd5b6040517fc39a055700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048201915081806020019051810190610c789190613bbb565b606081471015612cb9576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff168460405160006040518083038185875af1925050503d8060008114612d14576040519150601f19603f3d011682016040523d82523d6000602084013e612d19565b606091505b50915091508115612d2c57809250612d6e565b805115612d3c5780518082602001fd5b6040517f3204506f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b60405173ffffffffffffffffffffffffffffffffffffffff838116602483015260448201839052610eea91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506130df565b600073ffffffffffffffffffffffffffffffffffffffff8216612e1c57506000919050565b8173ffffffffffffffffffffffffffffffffffffffff16635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b82573d6000803e3d6000fd5b612e6f610eef565b6040517f7c98a9f400000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690637c98a9f490602401600060405180830381600087803b158015612ed857600080fd5b505af1158015610ed9573d6000803e3d6000fd5b6040805160018082528183019092526000916020808301908036833750507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa8549192505073ffffffffffffffffffffffffffffffffffffffff1681600081518110612f5957612f59613e93565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050610d63612f9e610eef565b82611afd565b600080612fb2868686612a16565b9050612fbd83613175565b8015612fd9575060008480612fd457612fd4613e64565b868809115b15612fec57612fe9600182613ec2565b90505b95945050505050565b60008183106130045781610c75565b5090919050565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b30000000000000000000000000000000000000000000000000000000017905261309784826131a2565b610ed95760405173ffffffffffffffffffffffffffffffffffffffff8481166024830152600060448301526130d991869182169063095ea7b390606401612db0565b610ed984825b600061310173ffffffffffffffffffffffffffffffffffffffff84168361325f565b905080516000141580156131265750808060200190518101906131249190613ed5565b155b15610eea576040517f5274afe700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015260240161131d565b6000600282600381111561318b5761318b613606565b6131959190613ef0565b60ff166001149050919050565b60008060008473ffffffffffffffffffffffffffffffffffffffff16846040516131cc9190613f39565b6000604051808303816000865af19150503d8060008114613209576040519150601f19603f3d011682016040523d82523d6000602084013e61320e565b606091505b50915091508180156132385750805115806132385750808060200190518101906132389190613ed5565b8015612fec57505050505073ffffffffffffffffffffffffffffffffffffffff163b151590565b6060610c7583836000846000808573ffffffffffffffffffffffffffffffffffffffff1684866040516132929190613f39565b60006040518083038185875af1925050503d80600081146132cf576040519150601f19603f3d011682016040523d82523d6000602084013e6132d4565b606091505b50915091506132e48683836132ee565b9695505050505050565b606082613303576132fe8261337d565b612b0b565b8151158015613327575073ffffffffffffffffffffffffffffffffffffffff84163b155b15613376576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015260240161131d565b5080612b0b565b80511561338d5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461117d57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613457576134576133e1565b604052919050565b600082601f83011261347057600080fd5b813567ffffffffffffffff81111561348a5761348a6133e1565b6134bb60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613410565b8181528460208386010111156134d057600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600080600060e0888a03121561350857600080fd5b8735613513816133bf565b96506020880135613523816133bf565b95506040880135613533816133bf565b9450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff81111561356457600080fd5b6135708a828b0161345f565b91505092959891949750929550565b60008060008060008060c0878903121561359857600080fd5b86356135a3816133bf565b955060208701356135b3816133bf565b945060408701356135c3816133bf565b9350606087013592506080870135915060a087013567ffffffffffffffff8111156135ed57600080fd5b6135f989828a0161345f565b9150509295509295509295565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020810160038310613670577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b6000806000806080858703121561368c57600080fd5b8435613697816133bf565b935060208501356136a7816133bf565b93969395505050506040820135916060013590565b600080604083850312156136cf57600080fd5b82356136da816133bf565b946020939093013593505050565b600080600080600080600060c0888a03121561370357600080fd5b873561370e816133bf565b9650602088013561371e816133bf565b955060408801359450606088013593506080880135925060a088013567ffffffffffffffff8082111561375057600080fd5b818a0191508a601f83011261376457600080fd5b81358181111561377357600080fd5b8b602082850101111561378557600080fd5b60208301945080935050505092959891949750929550565b6000806000606084860312156137b257600080fd5b83356137bd816133bf565b925060208401356137cd816133bf565b929592945050506040919091013590565b600080600080600060a086880312156137f657600080fd5b8535613801816133bf565b94506020860135613811816133bf565b93506040860135925060608601359150608086013567ffffffffffffffff81111561383b57600080fd5b6138478882890161345f565b9150509295509295909350565b60005b8381101561386f578181015183820152602001613857565b50506000910152565b60008151808452613890816020860160208601613854565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c756020830184613878565b6000602082840312156138e757600080fd5b8135612b0b816133bf565b600080600080600060a0868803121561390a57600080fd5b8535613915816133bf565b94506020860135613925816133bf565b93506040860135613935816133bf565b925060608601356003811061394957600080fd5b949793965091946080013592915050565b600067ffffffffffffffff821115613974576139746133e1565b5060051b60200190565b6000806040838503121561399157600080fd5b823561399c816133bf565b915060208381013567ffffffffffffffff8111156139b957600080fd5b8401601f810186136139ca57600080fd5b80356139dd6139d88261395a565b613410565b81815260059190911b820183019083810190888311156139fc57600080fd5b928401925b82841015613a23578335613a14816133bf565b82529284019290840190613a01565b80955050505050509250929050565b6020808252825182820181905260009190848201906040850190845b81811015613a6a57835183529284019291840191600101613a4e565b50909695505050505050565b60008060408385031215613a8957600080fd5b8235613a94816133bf565b91506020830135613aa4816133bf565b809150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015283608083015260c060a0830152613afa60c0830184613878565b98975050505050505050565b600080600060608486031215613b1b57600080fd5b8351613b26816133bf565b602085015160409095015190969495509392505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015283606083015260a06080830152613b8260a0830184613878565b979650505050505050565b60008060408385031215613ba057600080fd5b8251613bab816133bf565b6020939093015192949293505050565b600060208284031215613bcd57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610c7857610c78613bd4565b60008060408385031215613c2957600080fd5b505080516020909101519092909150565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015284604082015283606082015260a060808201528160a0820152818360c0830137600081830160c090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010195945050505050565b73ffffffffffffffffffffffffffffffffffffffff851681528360208201528260408201526080606082015260006132e46080830184613878565b600060208284031215613d0657600080fd5b8151612b0b816133bf565b600080600060608486031215613d2657600080fd5b8351925060208401519150604084015190509250925092565b6020808252825182820181905260009190848201906040850190845b81811015613a6a57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613d5b565b60006020808385031215613da057600080fd5b825167ffffffffffffffff811115613db757600080fd5b8301601f81018513613dc857600080fd5b8051613dd66139d88261395a565b81815260059190911b82018301908381019087831115613df557600080fd5b928401925b82841015613b8257835182529284019290840190613dfa565b80518015158114613e2357600080fd5b919050565b600080600060608486031215613e3d57600080fd5b613e4684613e13565b925060208401519150613e5b60408501613e13565b90509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80820180821115610c7857610c78613bd4565b600060208284031215613ee757600080fd5b610c7582613e13565b600060ff831680613f2a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8060ff84160691505092915050565b60008251613f4b818460208701613854565b919091019291505056fea2646970667358221220cd7946c4f5ee4da3effbe457d3a6a7f44610d04fa64a6b9b46f8ea2b8643bf0364736f6c634300081800330000000000000000000000004300000000000000000000000000000000000002000000000000000000000000f237c20584daca970498917470864f4d027de4ca0000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd800000000000000000000000000454c0c1cf7be9341d82ce0f16979b8689ed4aad0

Deployed Bytecode

0x6080604052600436106102a45760003560e01c806382ccd3301161016e578063beeb3261116100cb578063e2eefd3d1161007f578063edabb82d11610064578063edabb82d146107f1578063eee24219146107f9578063f1a2e8491461084357600080fd5b8063e2eefd3d146107c9578063e7b4e734146107dc57600080fd5b8063cd48069b116100b0578063cd48069b1461077f578063d00448ec14610794578063dc3be667146107b457600080fd5b8063beeb326114610757578063cbefbc261461076c57600080fd5b8063b2bd6b5011610122578063b627fbb911610107578063b627fbb91461071e578063bbdfbc4714610731578063bdaab1a61461074457600080fd5b8063b2bd6b50146106cb578063b440ae7b146106fe57600080fd5b80639080037311610153578063908003731461061b57806393f0899a14610665578063a6b204c1146106ab57600080fd5b806382ccd330146105a55780638c7c9e0c146105f457600080fd5b8063378e38bf1161021c578063559c47e1116101d05780636eb73f74116101b55780636eb73f74146105355780636f307dc314610548578063708322591461059257600080fd5b8063559c47e11461050d5780635fe3b5671461052057600080fd5b80633fc8cef3116102015780633fc8cef3146104be5780634808034e146104e55780634c711a2a146104f857600080fd5b8063378e38bf146104a15780633da8dddb146104b457600080fd5b8063243cd14e116102735780632c86d98e116102585780632c86d98e146104515780632fd3858c14610466578063345026821461048e57600080fd5b8063243cd14e146103d7578063295a5212146103fa57600080fd5b8063037581e6146102b0578063136bcd12146102fa578063175e1a7d146103395780631a32aad61461038d57600080fd5b366102ab57005b600080fd5b6102c36102be3660046134ed565b610858565b6040805173ffffffffffffffffffffffffffffffffffffffff90941684526020840192909252908201526060015b60405180910390f35b61030d61030836600461357f565b61091b565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152016102f1565b34801561034557600080fd5b507f00000000000000000000000043000000000000000000000000000000000000025b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102f1565b34801561039957600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff16610368565b3480156103e357600080fd5b506103ec6109d6565b6040519081526020016102f1565b34801561040657600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfab5474010000000000000000000000000000000000000000900460ff166040516102f19190613635565b34801561045d57600080fd5b506103ec610ad0565b610479610474366004613676565b610b0c565b604080519283526020830191909152016102f1565b6103ec61049c3660046136bc565b610bce565b6103ec6104af3660046136bc565b610c7e565b6104bc610cde565b005b3480156104ca57600080fd5b50734300000000000000000000000000000000000004610368565b6102c36104f33660046136e8565b610d67565b34801561050457600080fd5b506103ec610df7565b6104bc61051b36600461379d565b610e92565b34801561052c57600080fd5b50610368610eef565b6103ec6105433660046136bc565b610f3c565b34801561055457600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfaa5473ffffffffffffffffffffffffffffffffffffffff16610368565b61030d6105a03660046137de565b610f9d565b3480156105b157600080fd5b5060408051808201909152600881527f4c6f6f706f6f6f7200000000000000000000000000000000000000000000000060208201525b6040516102f191906138c2565b34801561060057600080fd5b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee610368565b34801561062757600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfab5473ffffffffffffffffffffffffffffffffffffffff16610368565b34801561067157600080fd5b5060408051808201909152600f81527f4c6f6f706f6f6f724d6f64756c6544000000000000000000000000000000000060208201526105e7565b3480156106b757600080fd5b506104bc6106c63660046136bc565b61104f565b3480156106d757600080fd5b507f0000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd800610368565b34801561070a57600080fd5b506104bc6107193660046138d5565b6110fb565b6104bc61072c3660046138d5565b611180565b61047961073f36600461379d565b6111cb565b6103ec6107523660046136bc565b611285565b34801561076357600080fd5b506104bc6112db565b6104bc61077a3660046138f2565b611326565b34801561078b57600080fd5b506103ec611980565b6107a76107a236600461397e565b611afd565b6040516102f19190613a32565b3480156107c057600080fd5b506104bc611bb7565b6104bc6107d7366004613a76565b611cc7565b3480156107e857600080fd5b50610368611da9565b6103ec611df1565b34801561080557600080fd5b507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa95473ffffffffffffffffffffffffffffffffffffffff16610368565b34801561084f57600080fd5b506103ec612902565b600080808961086889828a61294c565b6040517fc27fe2a900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063c27fe2a9906108c4908d908d908d908d908d908d90600401613aaf565b6060604051808303816000875af11580156108e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109079190613b06565b919d909c50909a5098505050505050505050565b6000808761092a87828861294c565b6040517fb97012f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063b97012f790610984908b908b908b908b908b90600401613b3d565b60408051808303816000875af11580156109a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c69190613b8d565b909a909950975050505050505050565b600080610a177fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b905073ffffffffffffffffffffffffffffffffffffffff8116610a3c57600091505090565b6040517f95dd919300000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8216906395dd919390602401602060405180830381865afa158015610aa6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aca9190613bbb565b91505090565b600080610adb611980565b90506000610ae76109d6565b9050610b0582670de0b6b3a7640000610b008483613c03565b612a16565b9250505090565b600080610b22610b1b87612b12565b878661294c565b6040517f58752e1600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201869052604482018590528716906358752e169060640160408051808303816000875af1158015610b9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc19190613c16565b9097909650945050505050565b6000610be3610bdc84612ba6565b848461294c565b6040517f0e7527020000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff841690630e752702906024015b6020604051808303816000875af1158015610c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c759190613bbb565b90505b92915050565b6000610c8b83848461294c565b6040517fdb006a750000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff84169063db006a7590602401610c32565b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa86000610d09610ad0565b9050610d13611df1565b50600382015482546002840154610d639273ffffffffffffffffffffffffffffffffffffffff80821693811692169074010000000000000000000000000000000000000000900460ff1685611326565b5050565b6000806000808a90508073ffffffffffffffffffffffffffffffffffffffff16637eac08c78a8c8c8c8c8c8c6040518863ffffffff1660e01b8152600401610db496959493929190613c3a565b60606040518083038185885af1158015610dd2573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906109079190613b06565b60003073ffffffffffffffffffffffffffffffffffffffff1663dc3be6676040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e4157600080fd5b505af1925050508015610e52575060015b610e8f573d808015610e80576040519150601f19603f3d011682016040523d82523d6000602084013e610e85565b606091505b50610aca81612c16565b90565b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff831601610edf57610ed98382612c7d565b50505050565b610eea828483612d76565b505050565b6000610f37610f327fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b612df7565b905090565b6000610f4a610bdc84612ba6565b6040517fa0712d680000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff84169063a0712d6890602401610c32565b6040517fff6926ed0000000000000000000000000000000000000000000000000000000081526000908190879073ffffffffffffffffffffffffffffffffffffffff82169063ff6926ed908890610ffe908b9083908b908b90600401613cb9565b604080518083038185885af115801561101b573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906110409190613b8d565b90999098509650505050505050565b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa8600061107a610ad0565b9050611084611df1565b5060028201546110ac90859073ffffffffffffffffffffffffffffffffffffffff1685610e92565b600382015482546002840154610ed99273ffffffffffffffffffffffffffffffffffffffff80821693811692169074010000000000000000000000000000000000000000900460ff1685611326565b611103612e67565b61117d8161110f610eef565b73ffffffffffffffffffffffffffffffffffffffff166310fe9ae86040518163ffffffff1660e01b8152600401602060405180830381865afa158015611159573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d79190613cf4565b50565b611188611df1565b50611103816107d77fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfaa5473ffffffffffffffffffffffffffffffffffffffff1690565b6000806111e16111da86612b12565b868561294c565b6040517fe862ca9900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301526024820185905286169063e862ca999060440160408051808303816000875af1158015611255573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112799190613c16565b90969095509350505050565b6040517fc5ebeaec0000000000000000000000000000000000000000000000000000000081526004810182905260009073ffffffffffffffffffffffffffffffffffffffff84169063c5ebeaec90602401610c32565b60006112e5611df1565b9050806040517ff08ba1fc00000000000000000000000000000000000000000000000000000000815260040161131d91815260200190565b60405180910390fd5b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa9547fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa89073ffffffffffffffffffffffffffffffffffffffff161561138f5761138d611df1565b505b6003810180548491907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000008360028111156113e2576113e2613606565b0217905550805473ffffffffffffffffffffffffffffffffffffffff8087167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161783556002830180548783169084161790556003830180549189169190921617905561144f612eec565b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8516016114c3576114aa73430000000000000000000000000000000000000447612c7d565b5073430000000000000000000000000000000000000493505b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8616906370a0823190602401602060405180830381865afa158015611530573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115549190613bbb565b9050600061156b8285670de0b6b3a7640000612a16565b9050600185600281111561158157611581613606565b036115f657604080516000808252602082019092526115ab908a9083908a90879083908190610858565b50506001850180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055505b600285600281111561160a5761160a613606565b0361167c5760408051600080825260208201909252611632908a9083908a908790839061091b565b506001850180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055505b6116868783610f3c565b506116918282613c03565b9050600061169d610eef565b73ffffffffffffffffffffffffffffffffffffffff16637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170b9190613cf4565b73ffffffffffffffffffffffffffffffffffffffff1663fc57d4df6117647fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa1580156117cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f19190613bbb565b90505b8115611975576000611804610eef565b6040517f5ec88c7900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690635ec88c7990602401606060405180830381865afa158015611870573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118949190613d11565b509150506118ad81670de0b6b3a7640000846000612fa4565b90506118bd896107528584612ff5565b506118c6611da9565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611932573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119569190613bbb565b93506119628985610f3c565b5061196d8484613c03565b9250506117f4565b505050505050505050565b6000806119c17fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b905073ffffffffffffffffffffffffffffffffffffffff81166119e657600091505090565b60008173ffffffffffffffffffffffffffffffffffffffff1663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a579190613bbb565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611ac4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ae89190613bbb565b9250610b058382670de0b6b3a7640000612a16565b6040517fc299823800000000000000000000000000000000000000000000000000000000815260609073ffffffffffffffffffffffffffffffffffffffff84169063c299823890611b52908590600401613d3f565b6000604051808303816000875af1158015611b71573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610c759190810190613d8d565b611bbf612e67565b6000611bc9610eef565b73ffffffffffffffffffffffffffffffffffffffff166310fe9ae86040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c379190613cf4565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611ca3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e59190613bbb565b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff821601611d0e57610eea8247612c7d565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610d63908290849073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611d80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da49190613bbb565b612d76565b6000610f37611dec7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b612ba6565b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa880546000919073ffffffffffffffffffffffffffffffffffffffff1682611e37611da9565b90506000611e43610eef565b905060008373ffffffffffffffffffffffffffffffffffffffff1663bd6d894d6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611e94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb89190613bbb565b905060008273ffffffffffffffffffffffffffffffffffffffff16637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f2b9190613cf4565b73ffffffffffffffffffffffffffffffffffffffff1663fc57d4df611f847fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015611fed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120119190613bbb565b905060008373ffffffffffffffffffffffffffffffffffffffff16638e8f294b61206f7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa85473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401606060405180830381865afa1580156120d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120fc9190613e28565b506040517f17bfdfbc0000000000000000000000000000000000000000000000000000000081523060048201529092506000915073ffffffffffffffffffffffffffffffffffffffff8816906317bfdfbc906024016020604051808303816000875af1158015612170573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121949190613bbb565b90505b80156123ef576040517f5ec88c7900000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff871690635ec88c7990602401606060405180830381865afa15801561220a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061222e9190613d11565b5091505061224781670de0b6b3a7640000856000612fa4565b905061225e81670de0b6b3a7640000866000612fa4565b905061227581670de0b6b3a7640000876000612fa4565b895490915061229a9073ffffffffffffffffffffffffffffffffffffffff1682610c7e565b506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8916906370a0823190602401602060405180830381865afa158015612308573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061232c9190613bbb565b8a549091506123559073ffffffffffffffffffffffffffffffffffffffff1661049c8386612ff5565b506040517f17bfdfbc00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8a16906317bfdfbc906024016020604051808303816000875af11580156123c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e69190613bbb565b92505050612197565b87546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526124889173ffffffffffffffffffffffffffffffffffffffff90811691908a16906370a0823190602401602060405180830381865afa158015612464573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104af9190613bbb565b50600188015473ffffffffffffffffffffffffffffffffffffffff16156126d8576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906125d69073ffffffffffffffffffffffffffffffffffffffff8916906370a0823190602401602060405180830381865afa15801561251a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253e9190613bbb565b8a60010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ba5d30786040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d19190613bbb565b612ff5565b9050600160038a015474010000000000000000000000000000000000000000900460ff16600281111561260b5761260b613606565b0361264157600389015460018a015461263e9173ffffffffffffffffffffffffffffffffffffffff9081169116836111cb565b50505b600260038a015474010000000000000000000000000000000000000000900460ff16600281111561267457612674613606565b036126ac57600389015460018a01546126a99173ffffffffffffffffffffffffffffffffffffffff9081169116836000610b0c565b50505b506001880180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61272b7fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfaa5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1603612858576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152734300000000000000000000000000000000000004906370a0823190602401602060405180830381865afa1580156127ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127d29190613bbb565b6040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810182905290995073430000000000000000000000000000000000000490632e1a7d4d90602401600060405180830381600087803b15801561283b57600080fd5b505af115801561284f573d6000803e3d6000fd5b505050506128ef565b60028801546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa1580156128c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ec9190613bbb565b98505b6128f7612e67565b505050505050505090565b60003073ffffffffffffffffffffffffffffffffffffffff1663beeb32616040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e4157600080fd5b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015282919085169063dd62ed3e90604401602060405180830381865afa1580156129c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129e59190613bbb565b1015610eea57610eea83837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61300b565b6000838302817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8587098281108382030391505080600003612a6b57838281612a6157612a61613e64565b0492505050612b0b565b808411612aa4576040517f227bc15300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150505b9392505050565b600073ffffffffffffffffffffffffffffffffffffffff8216612b3757506000919050565b8173ffffffffffffffffffffffffffffffffffffffff1663d7a433aa6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c789190613cf4565b600073ffffffffffffffffffffffffffffffffffffffff8216612bcb57506000919050565b8173ffffffffffffffffffffffffffffffffffffffff16636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b82573d6000803e3d6000fd5b60008151602414612c6357815115612c315781518083602001fd5b6040517fc39a055700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048201915081806020019051810190610c789190613bbb565b606081471015612cb9576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff168460405160006040518083038185875af1925050503d8060008114612d14576040519150601f19603f3d011682016040523d82523d6000602084013e612d19565b606091505b50915091508115612d2c57809250612d6e565b805115612d3c5780518082602001fd5b6040517f3204506f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b60405173ffffffffffffffffffffffffffffffffffffffff838116602483015260448201839052610eea91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506130df565b600073ffffffffffffffffffffffffffffffffffffffff8216612e1c57506000919050565b8173ffffffffffffffffffffffffffffffffffffffff16635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b82573d6000803e3d6000fd5b612e6f610eef565b6040517f7c98a9f400000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690637c98a9f490602401600060405180830381600087803b158015612ed857600080fd5b505af1158015610ed9573d6000803e3d6000fd5b6040805160018082528183019092526000916020808301908036833750507fee0af4000704d8fae2a5416fe137cd5d4ce428b76a732fe889c07a9d36dcdfa8549192505073ffffffffffffffffffffffffffffffffffffffff1681600081518110612f5957612f59613e93565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050610d63612f9e610eef565b82611afd565b600080612fb2868686612a16565b9050612fbd83613175565b8015612fd9575060008480612fd457612fd4613e64565b868809115b15612fec57612fe9600182613ec2565b90505b95945050505050565b60008183106130045781610c75565b5090919050565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b30000000000000000000000000000000000000000000000000000000017905261309784826131a2565b610ed95760405173ffffffffffffffffffffffffffffffffffffffff8481166024830152600060448301526130d991869182169063095ea7b390606401612db0565b610ed984825b600061310173ffffffffffffffffffffffffffffffffffffffff84168361325f565b905080516000141580156131265750808060200190518101906131249190613ed5565b155b15610eea576040517f5274afe700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015260240161131d565b6000600282600381111561318b5761318b613606565b6131959190613ef0565b60ff166001149050919050565b60008060008473ffffffffffffffffffffffffffffffffffffffff16846040516131cc9190613f39565b6000604051808303816000865af19150503d8060008114613209576040519150601f19603f3d011682016040523d82523d6000602084013e61320e565b606091505b50915091508180156132385750805115806132385750808060200190518101906132389190613ed5565b8015612fec57505050505073ffffffffffffffffffffffffffffffffffffffff163b151590565b6060610c7583836000846000808573ffffffffffffffffffffffffffffffffffffffff1684866040516132929190613f39565b60006040518083038185875af1925050503d80600081146132cf576040519150601f19603f3d011682016040523d82523d6000602084013e6132d4565b606091505b50915091506132e48683836132ee565b9695505050505050565b606082613303576132fe8261337d565b612b0b565b8151158015613327575073ffffffffffffffffffffffffffffffffffffffff84163b155b15613376576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015260240161131d565b5080612b0b565b80511561338d5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461117d57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613457576134576133e1565b604052919050565b600082601f83011261347057600080fd5b813567ffffffffffffffff81111561348a5761348a6133e1565b6134bb60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613410565b8181528460208386010111156134d057600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600080600060e0888a03121561350857600080fd5b8735613513816133bf565b96506020880135613523816133bf565b95506040880135613533816133bf565b9450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff81111561356457600080fd5b6135708a828b0161345f565b91505092959891949750929550565b60008060008060008060c0878903121561359857600080fd5b86356135a3816133bf565b955060208701356135b3816133bf565b945060408701356135c3816133bf565b9350606087013592506080870135915060a087013567ffffffffffffffff8111156135ed57600080fd5b6135f989828a0161345f565b9150509295509295509295565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020810160038310613670577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b6000806000806080858703121561368c57600080fd5b8435613697816133bf565b935060208501356136a7816133bf565b93969395505050506040820135916060013590565b600080604083850312156136cf57600080fd5b82356136da816133bf565b946020939093013593505050565b600080600080600080600060c0888a03121561370357600080fd5b873561370e816133bf565b9650602088013561371e816133bf565b955060408801359450606088013593506080880135925060a088013567ffffffffffffffff8082111561375057600080fd5b818a0191508a601f83011261376457600080fd5b81358181111561377357600080fd5b8b602082850101111561378557600080fd5b60208301945080935050505092959891949750929550565b6000806000606084860312156137b257600080fd5b83356137bd816133bf565b925060208401356137cd816133bf565b929592945050506040919091013590565b600080600080600060a086880312156137f657600080fd5b8535613801816133bf565b94506020860135613811816133bf565b93506040860135925060608601359150608086013567ffffffffffffffff81111561383b57600080fd5b6138478882890161345f565b9150509295509295909350565b60005b8381101561386f578181015183820152602001613857565b50506000910152565b60008151808452613890816020860160208601613854565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c756020830184613878565b6000602082840312156138e757600080fd5b8135612b0b816133bf565b600080600080600060a0868803121561390a57600080fd5b8535613915816133bf565b94506020860135613925816133bf565b93506040860135613935816133bf565b925060608601356003811061394957600080fd5b949793965091946080013592915050565b600067ffffffffffffffff821115613974576139746133e1565b5060051b60200190565b6000806040838503121561399157600080fd5b823561399c816133bf565b915060208381013567ffffffffffffffff8111156139b957600080fd5b8401601f810186136139ca57600080fd5b80356139dd6139d88261395a565b613410565b81815260059190911b820183019083810190888311156139fc57600080fd5b928401925b82841015613a23578335613a14816133bf565b82529284019290840190613a01565b80955050505050509250929050565b6020808252825182820181905260009190848201906040850190845b81811015613a6a57835183529284019291840191600101613a4e565b50909695505050505050565b60008060408385031215613a8957600080fd5b8235613a94816133bf565b91506020830135613aa4816133bf565b809150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015283608083015260c060a0830152613afa60c0830184613878565b98975050505050505050565b600080600060608486031215613b1b57600080fd5b8351613b26816133bf565b602085015160409095015190969495509392505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015283606083015260a06080830152613b8260a0830184613878565b979650505050505050565b60008060408385031215613ba057600080fd5b8251613bab816133bf565b6020939093015192949293505050565b600060208284031215613bcd57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610c7857610c78613bd4565b60008060408385031215613c2957600080fd5b505080516020909101519092909150565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015284604082015283606082015260a060808201528160a0820152818360c0830137600081830160c090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010195945050505050565b73ffffffffffffffffffffffffffffffffffffffff851681528360208201528260408201526080606082015260006132e46080830184613878565b600060208284031215613d0657600080fd5b8151612b0b816133bf565b600080600060608486031215613d2657600080fd5b8351925060208401519150604084015190509250925092565b6020808252825182820181905260009190848201906040850190845b81811015613a6a57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613d5b565b60006020808385031215613da057600080fd5b825167ffffffffffffffff811115613db757600080fd5b8301601f81018513613dc857600080fd5b8051613dd66139d88261395a565b81815260059190911b82018301908381019087831115613df557600080fd5b928401925b82841015613b8257835182529284019290840190613dfa565b80518015158114613e2357600080fd5b919050565b600080600060608486031215613e3d57600080fd5b613e4684613e13565b925060208401519150613e5b60408501613e13565b90509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80820180821115610c7857610c78613bd4565b600060208284031215613ee757600080fd5b610c7582613e13565b600060ff831680613f2a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8060ff84160691505092915050565b60008251613f4b818460208701613854565b919091019291505056fea2646970667358221220cd7946c4f5ee4da3effbe457d3a6a7f44610d04fa64a6b9b46f8ea2b8643bf0364736f6c63430008180033

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

0000000000000000000000004300000000000000000000000000000000000002000000000000000000000000f237c20584daca970498917470864f4d027de4ca0000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd800000000000000000000000000454c0c1cf7be9341d82ce0f16979b8689ed4aad0

-----Decoded View---------------
Arg [0] : blast_ (address): 0x4300000000000000000000000000000000000002
Arg [1] : gasCollector_ (address): 0xf237c20584DaCA970498917470864f4d027de4ca
Arg [2] : blastPoints_ (address): 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800
Arg [3] : pointsOperator_ (address): 0x454c0C1CF7be9341d82ce0F16979B8689ED4AAD0

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000004300000000000000000000000000000000000002
Arg [1] : 000000000000000000000000f237c20584daca970498917470864f4d027de4ca
Arg [2] : 0000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd800
Arg [3] : 000000000000000000000000454c0c1cf7be9341d82ce0f16979b8689ed4aad0


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.