ETH Price: $2,518.21 (+2.78%)

Contract

0x2884647440710e81d338b6032314e32436777B47
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x61018060200553172024-06-09 15:59:3585 days ago1717948775IN
 Create: Account
0 ETH0.0321662511.75778632

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Account

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
paris EvmVersion
File 1 of 27 : Account.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";

import {IJitCompiler} from "contracts/interfaces/accountAbstraction/interpreter/IJitCompiler.sol";
import {
    Asset,
    IAccount,
    Script
} from "contracts/interfaces/accountAbstraction/marginAccount/IAccount.sol";
import {
    AddressMustNotBeZero,
    AmountMustNotBeZero,
    ArrayMustNotBeEmpty,
    UnauthorizedAccount
} from "contracts/interfaces/base/CommonErrors.sol";
import {IInitializable} from "contracts/interfaces/common/IInitializable.sol";

import {OwnableAssignable} from "contracts/base/auth/OwnableAssignable.sol";
import {State, StateMachine} from "contracts/base/StateMachine.sol";
import {Command, CommandExecutor} from "contracts/libraries/CommandLibrary.sol";

import {CommandSafeExecutor} from "./base/CommandSafeExecutor.sol";

contract Account is
    IAccount,
    IInitializable,
    Initializable,
    OwnableAssignable,
    StateMachine,
    CommandSafeExecutor
{
    using SafeTransferLib for address;
    using CommandExecutor for Command[];

    Asset public override leverage;
    uint256 private supplied;
    uint256 private stateSwitchTime;

    address public immutable marginEngine;
    IJitCompiler public immutable compiler;

    /* solhint-disable immutable-vars-naming */
    State private immutable STATE_CLOSED = newStateFromId(0);
    State private immutable STATE_REGISTERED = newStateFromId(1);
    State private immutable STATE_OPENED = newStateFromId(2);
    State private immutable STATE_SUSPENDED = newStateFromId(3);
    /* solhint-enable immutable-vars-naming */

    modifier onlyMarginEngine() {
        if (msg.sender != marginEngine) revert UnauthorizedAccount(msg.sender);
        _;
    }

    constructor(address _marginEngine, address _compliance) CommandSafeExecutor(_compliance) {
        if (_marginEngine == address(0) || _compliance == address(0)) revert AddressMustNotBeZero();
        marginEngine = _marginEngine;
        // NOTE: jitCompiler share diamond storage with compliance,
        // they are both facets in whitelisting diamond
        compiler = IJitCompiler(_compliance);
    }

    function initialize() external override reinitializer(2) {
        // X -> REGISTERED
        createTransition(STATE_UNDEFINED, STATE_REGISTERED, this.registerAccount);
        createTransition(STATE_CLOSED, STATE_REGISTERED, this.registerAccount);
        // X -> OPENED
        createTransition(STATE_REGISTERED, STATE_OPENED, this.openAccount);
        // X -> SUSPENDED
        createTransition(STATE_REGISTERED, STATE_SUSPENDED, this.suspendUnopenedAccount);
        createTransition(STATE_OPENED, STATE_SUSPENDED, this.suspendAccount);
        // X -> CLOSED
        createTransition(STATE_SUSPENDED, STATE_CLOSED, this.closeAccount);
    }

    receive() external payable override {}

    function register(
        address _user,
        Asset[] calldata _collateral,
        Asset calldata _leverage
    ) external payable override onlyMarginEngine onlyState(STATE_UNDEFINED | STATE_CLOSED) {
        for (uint256 i; i < _collateral.length; i++) {
            _collateral[i].enforceReceived();
        }
        changeState(STATE_REGISTERED, abi.encode(_user, _collateral, _leverage));
    }

    function supply(
        uint256 _amount
    )
        external
        payable
        override
        onlyMarginEngine
        onlyState(STATE_REGISTERED)
        returns (bool allocationSuccess_)
    {
        if (_amount == 0) revert AmountMustNotBeZero(leverage.token);
        supplied += _amount;

        emit LeverageSupplied(owner(), _amount, int256(leverage.amount) - int256(supplied));

        if (supplied == leverage.amount) {
            changeState(STATE_OPENED);
            allocationSuccess_ = true;
        } else if (supplied > leverage.amount) {
            changeState(STATE_SUSPENDED);
        }
    }

    function tryClose(
        Script[] calldata strategy
    )
        external
        override
        onlyMarginEngine
        onlyState(STATE_OPENED | STATE_SUSPENDED)
        returns (bool success_)
    {
        if (strategy.length == 0) revert ArrayMustNotBeEmpty();
        if (currentState() == STATE_OPENED) changeState(STATE_SUSPENDED);

        for (uint256 i; i < strategy.length; i++) {
            Command[] memory cmds = compiler.compile(strategy[i]);
            // solhint-disable-next-line no-empty-blocks
            try cmds.execute() {} catch {
                return success_ = false;
            }
        }

        changeState(STATE_CLOSED);
        return success_ = true;
    }

    function execute(Command calldata _cmd) external override onlyOwner onlyState(STATE_OPENED) {
        CommandSafeExecutor.validateAndExecute(_cmd);
    }

    function stateInfo() external view override returns (State, uint256) {
        return (currentState(), stateSwitchTime);
    }

    function allocationInfo()
        external
        view
        override
        returns (State, uint256, address, address, uint256)
    {
        return (currentState(), stateSwitchTime, owner(), leverage.token, leverage.amount);
    }

    function owner() public view override returns (address) {
        return _owner();
    }

    // State Machine Transitions
    // NOTE: You MUST NOT change names of these functions because it
    // will break state machine setup in all proxies. To change the
    // name you must increment version of this contract and call
    // reinitializer(nextVersion) for all proxies.

    function registerAccount(bytes calldata _args) external transition {
        _beforeStateSwitch();

        (address user, Asset[] memory collateral, Asset memory _leverage) = abi.decode(
            _args,
            (address, Asset[], Asset)
        );

        _assignOwner(user);
        leverage = _leverage;
        emit AccountRegistered(user, collateral, _leverage);
    }

    function openAccount(bytes calldata) external transition {
        _beforeStateSwitch();

        delete supplied;
        emit AccountOpened(owner());
    }

    function suspendAccount(bytes calldata) external transition {
        _beforeStateSwitch();

        emit AccountSuspended(owner());
    }

    function suspendUnopenedAccount(bytes calldata) external transition {
        _beforeStateSwitch();

        delete supplied;

        emit AccountSuspended(owner());
    }

    function closeAccount(bytes calldata) external transition {
        _beforeStateSwitch();

        address previousOwner = owner();

        _unassignOwner();
        delete leverage;

        emit AccountClosed(previousOwner);
    }

    function _beforeStateSwitch() private {
        stateSwitchTime = block.timestamp; // solhint-disable-line not-rely-on-time
    }
}

File 2 of 27 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE =
        0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

File 3 of 27 : 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 27 : 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 5 of 27 : CommandSafeExecutor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
import {LibString} from "solady/src/utils/LibString.sol";

import {AddressMustNotBeZero} from "contracts/interfaces/base/CommonErrors.sol";

import {CalldataLibrary} from "contracts/libraries/CalldataLibrary.sol";
import {Command, CommandLibrary} from "contracts/libraries/CommandLibrary.sol";
import {SafeCall} from "contracts/libraries/SafeCall.sol";

contract CommandSafeExecutor {
    using LibString for address;
    using CalldataLibrary for bytes;
    using CommandLibrary for Command[];
    using SafeCall for Command;
    using SafeCall for Command[];

    address public immutable compliance;
    string private constant EMPTY_DATA_SIG_PREFIX = "emptyDataForOperator";

    constructor(address _compliance) {
        if (_compliance == address(0)) revert AddressMustNotBeZero();
        compliance = _compliance;
    }

    function validateAndExecute(Command calldata _cmd) internal {
        execute(_cmd, validate(_cmd));
    }

    function execute(Command calldata _cmd, bytes memory _encodedCmds) private {
        Command[] memory cmdsToExecute = abi.decode(_encodedCmds, (Command[]));

        cmdsToExecute.last().value = _cmd.value;
        cmdsToExecute.safeCallAll();
    }

    function validate(Command calldata _cmd) private returns (bytes memory result_) {
        Command memory cmdToValidate = Command({
            target: compliance,
            value: 0,
            payload: constructPayloadWithOperator(_cmd)
        });

        return cmdToValidate.safeCall();
    }

    function constructPayloadWithOperator(
        Command calldata _cmd
    ) private pure returns (bytes memory) {
        address operator = _cmd.target;

        bytes memory payload = _cmd.payload.length > 0
            ? _cmd.payload
            : constructPayloadForFallback(operator.toHexString());

        return payload.appendWord(operator);
    }

    function constructPayloadForFallback(
        string memory _operator
    ) private pure returns (bytes memory) {
        string memory signature = string.concat(EMPTY_DATA_SIG_PREFIX, _operator, "()");
        bytes4 selector = bytes4(keccak256(bytes(signature)));
        return abi.encodePacked(selector);
    }
}

File 6 of 27 : OwnableAssignable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {Address} from "contracts/libraries/Address.sol";
import {OwnableReadonly} from "./OwnableReadonly.sol";

abstract contract OwnableAssignable is OwnableReadonly {
    using Address for bytes32;

    bytes32 private constant OWNER = keccak256("OwnableAssignable owner slot V1");

    event OwnerHasBeenAssigned(address indexed previousOwner, address indexed newOwner);
    event OwnerHasBeenUnassigned(address indexed previousOwner);

    error NewOwnerMustBeNonZeroAddress();
    error OwnerDoesNotExist();

    function _assignOwner(address _newOwner) internal returns (address previousOwner_) {
        if (_newOwner == address(0)) revert NewOwnerMustBeNonZeroAddress();

        previousOwner_ = _owner();
        _ownerSlot().set(_newOwner);
        emit OwnerHasBeenAssigned(previousOwner_, _newOwner);
    }

    function _unassignOwner() internal returns (address previousOwner_) {
        if ((previousOwner_ = _owner()) == address(0)) revert OwnerDoesNotExist();

        _ownerSlot().set(address(0));
        emit OwnerHasBeenUnassigned(previousOwner_);
    }

    function _ownerSlot() internal pure virtual override returns (bytes32) {
        return OWNER;
    }
}

File 7 of 27 : OwnableReadonly.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {UnauthorizedAccount} from "contracts/interfaces/base/CommonErrors.sol";
import {Address} from "contracts/libraries/Address.sol";

/**
 * @dev We intentionally do not expose "owner()" publicly
 * due to possible conflicts with "OwnershipFacet"
 * https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/facets/OwnershipFacet.sol
 */
abstract contract OwnableReadonly {
    using Address for bytes32;

    modifier onlyOwner() {
        enforceIsContractOwner();
        _;
    }

    function _owner() internal view returns (address) {
        return _ownerSlot().get();
    }

    function _ownerSlot() internal pure virtual returns (bytes32);

    function enforceIsContractOwner() private view {
        if (msg.sender != _owner()) revert UnauthorizedAccount(msg.sender);
    }
}

File 8 of 27 : StateMachine.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {LibBit} from "solady/src/utils/LibBit.sol";

type State is uint256;

using {and as &, neq as !=, eq as ==, or as |, includes, isInitialized, isValid} for State global;

function and(State self, State value) pure returns (State) {
    return State.wrap(State.unwrap(self) & State.unwrap(value));
}

function neq(State self, State value) pure returns (bool) {
    return State.unwrap(self) != State.unwrap(value);
}

function eq(State self, State value) pure returns (bool) {
    return State.unwrap(self) == State.unwrap(value);
}

function or(State self, State value) pure returns (State) {
    return State.wrap(State.unwrap(self) | State.unwrap(value));
}

function includes(State bitmap, State state) pure returns (bool) {
    return State.unwrap(bitmap) & State.unwrap(state) != 0;
}

function isInitialized(State self) pure returns (bool answer_) {
    return State.unwrap(self) != 0;
}

function isValid(State self) pure returns (bool) {
    // most significant bit is reserved for the undefined state
    uint256 mask = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    return LibBit.isPo2(State.unwrap(self) & mask);
}

abstract contract StateMachine {
    struct Storage {
        State currentState;
        mapping(bytes32 transitionId => function(bytes memory) external transition) transitions;
    }

    // Undefined state cannot be zero because it will break bitmap comparison math in `onlyState`
    State internal immutable STATE_UNDEFINED = newStateFromIdUnchecked(STATE_UNDEFINED_ID); // solhint-disable-line immutable-vars-naming

    uint8 private constant STATE_UNDEFINED_ID = type(uint8).max;
    bytes32 private constant STORAGE_SLOT = keccak256("StateMachine storage slot V2");

    event StateChanged(State from, State to);

    error TransitionAlreadyExists(State from, State to);
    error TransitionDoesNotExist(State from, State to);

    error UnexpectedState(State expectedStatesBitmap, State currentState);
    // If transition function exists on current contract
    // then it must be called only from the current contract.
    error HostedTransitionMustBeCalledFromSelf();
    // A valid state must be in form of 2^n, where n ∈ {x | x ∈ uint8, x < STATE_UNDEFINED_ID}.
    error InvalidState(State);
    error IdIsReservedForUndefinedState(uint256);

    modifier onlyState(State _expectedStatesBitmap) {
        if (!_expectedStatesBitmap.includes(currentState()))
            revert UnexpectedState(_expectedStatesBitmap, currentState());
        _;
    }

    modifier transition() {
        if (msg.sender != address(this)) revert HostedTransitionMustBeCalledFromSelf();
        _;
    }

    function createTransition(
        State _from,
        State _to,
        function(bytes memory) external _transition
    ) internal {
        bytes32 id = getTransitionId(_from, _to);
        if (isTransitionExists(id)) revert TransitionAlreadyExists(_from, _to);

        _storage().transitions[id] = _transition;
    }

    function deleteTransition(State _from, State _to) internal {
        bytes32 id = getTransitionId(_from, _to);
        if (!isTransitionExists(id)) revert TransitionDoesNotExist(_from, _to);

        delete _storage().transitions[id];
    }

    function changeState(State _newState) internal {
        changeState(_newState, "");
    }

    function changeState(State _newState, bytes memory _transitionArgs) internal {
        if (!_newState.isValid()) revert InvalidState(_newState);

        bytes32 id = getTransitionId(currentState(), _newState);
        if (!isTransitionExists(id)) revert TransitionDoesNotExist(currentState(), _newState);

        emit StateChanged(currentState(), _newState);
        _storage().currentState = _newState;

        _storage().transitions[id](_transitionArgs);
    }

    function isTransitionExists(State _from, State _to) internal view returns (bool) {
        return isTransitionExists(getTransitionId(_from, _to));
    }

    function currentState() internal view returns (State currentState_) {
        currentState_ = _storage().currentState;
        // We substitute 0 with STATE_UNDEFINED here in order to avoid storage
        // initialization with default value to save gas
        if (!currentState_.isInitialized()) return currentState_ = STATE_UNDEFINED;
    }

    function newStateFromId(uint8 _stateId) internal pure returns (State) {
        if (_stateId == STATE_UNDEFINED_ID) revert IdIsReservedForUndefinedState(_stateId);
        return newStateFromIdUnchecked(_stateId);
    }

    function isTransitionExists(bytes32 _transitionId) private view returns (bool exists_) {
        mapping(bytes32 => function(bytes memory) external) storage map = _storage().transitions;
        assembly {
            // we won't use this memory location after keccak so it's safe to use 0x00 and 0x20
            mstore(0x00, _transitionId)
            mstore(0x20, map.slot)
            let position := keccak256(0x00, 64)
            // callback = map[_transition]
            let callback := sload(position)
            // exists_ = callback != null
            exists_ := iszero(iszero(callback))
        }
    }

    function getTransitionId(State _from, State _to) private view returns (bytes32) {
        if (_from != STATE_UNDEFINED && !_from.isValid()) revert InvalidState(_from);
        if (!_to.isValid()) revert InvalidState(_to);
        return keccak256(abi.encodePacked(_from, _to));
    }

    function newStateFromIdUnchecked(uint8 _stateId) private pure returns (State) {
        return State.wrap(1 << _stateId);
    }

    function _storage() private pure returns (Storage storage s_) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            s_.slot := slot
        }
    }
}

File 9 of 27 : Asset.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {AssetLibrary} from "contracts/libraries/AssetLibrary.sol";

/**
 * @title Asset
 * @dev Represents an asset with its token address and the amount.
 * @param token The address of the asset's token.
 * @param amount The amount of the asset.
 */
struct Asset {
    address token;
    uint256 amount;
}

using AssetLibrary for Asset global;

File 10 of 27 : IDecreasePositionEvaluator.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {Asset} from "contracts/libraries/AssetLibrary.sol";
import {Command} from "../Command.sol";
import {PositionDescriptor} from "./PositionDescriptor.sol";

interface IDecreasePositionEvaluator {
    /**
     * @notice Request structure for decreasing a position.
     * @dev `descriptor`: The [`PositionDescriptor`](/interfaces/accountAbstraction/interpreter/adapters/PositionDescriptor.sol/struct.PositionDescriptor.html)
     *  struct.
     * @dev `liquidity`: Abstract amount that can be interpreted differently in different protocols (e.g., amount of LP tokens to burn).
     * @dev `minOutput`: [`Asset`](/interfaces/accountAbstraction/compliance/Asset.sol/struct.Asset.html) array with minimum amounts that must be retrieved from the position.
     */
    struct DecreasePositionRequest {
        PositionDescriptor descriptor;
        uint256 liquidity;
        Asset[] minOutput;
    }

    /**
     * @notice Evaluate a decrease position request.
     * @param _operator Address which initiated the request
     * @param _request The [`DecreasePositionRequest`](#decreasepositionrequest) struct containing decrease position details.
     * @return cmds_ An array of [`Command`](../../Command.sol/struct.Command.html) to execute the request.
     */
    function evaluate(
        address _operator,
        DecreasePositionRequest calldata _request
    ) external returns (Command[] memory cmds_);
}

File 11 of 27 : IExchangeEvaluator.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

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

/**
 * @title IExchangeEvaluator
 * @notice Interface for compiling commands for token exchanges for different protocols.
 */
interface IExchangeEvaluator {
    /**
     * @notice Structure for an exchange token request.
     * @dev `path`: Encoded path of tokens to follow in the exchange, including pool identifiers.
     * 20 bytes(tokenA) + 4 byte(poolId_A_B) + 20 bytes(tokenB) + ...
     * ... + 4 byte(poolId_N-1_N) + 20 bytes(tokenN).
     * @dev `extraData`: Additional data specific to a particular protocol, such as the response from a 1Inch Exchange API.
     * @dev `amountIn`: The amount of tokenA to spend.
     * @dev `minAmountOut`: The minimum amount of tokenN to receive.
     * @dev `recipient`: The recipient of tokenN.
     */
    struct ExchangeRequest {
        bytes path;
        bytes extraData;
        uint256 amountIn;
        uint256 minAmountOut;
        address recipient;
    }

    /**
     * @notice Constructs an exchange token request.
     * @param _operator Address which initiated the request
     * @param _request The [`ExchangeRequest`](#exchangerequest) struct containing exchange token details.
     * @return cmds_ An array of [`Command`](../../Command.sol/struct.Command.html) to execute the request.
     */
    function evaluate(
        address _operator,
        ExchangeRequest calldata _request
    ) external view returns (Command[] memory cmds_);
}

File 12 of 27 : IIncreasePositionEvaluator.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {Asset} from "contracts/libraries/AssetLibrary.sol";
import {Command} from "../Command.sol";
import {PositionDescriptor} from "./PositionDescriptor.sol";

interface IIncreasePositionEvaluator {
    /**
     * @notice Structure for an increase position request.
     * @dev `descriptor`: The [`PositionDescriptor`](/interfaces/accountAbstraction/interpreter/adapters/PositionDescriptor.sol/struct.PositionDescriptor.html)
     *  struct.
     * @dev `input`: An array of [`Asset`](/interfaces/accountAbstraction/compliance/Asset.sol/struct.Asset.html) representing the token-amounts that will be added to the position.
     * @dev `minLiquidityOut`: An abstract amount that can be interpreted differently in different protocols (e.g., minimum amount of LP tokens to receive).
     */
    struct IncreasePositionRequest {
        PositionDescriptor descriptor;
        Asset[] input;
        uint256 minLiquidityOut;
    }

    /**
     * @notice Evaluate a increase position request.
     * @param _operator Address which initiated the request
     * @param _request The [`IncreasePositionRequest`](#increasepositionrequest) struct containing increase position details.
     * @return cmds_ An array of [`Command`](../../Command.sol/struct.Command.html) to execute the request.
     */
    function evaluate(
        address _operator,
        IncreasePositionRequest calldata _request
    ) external returns (Command[] memory cmds_);
}

File 13 of 27 : PositionDescriptor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// TODO CRYPTO-145: Possibly move into appropriate interface?
/**
 * @notice Used to determine the required position for an operation.
 * @dev `poolId`: An identifier that is unique within a single protocol.
 * @dev `extraData`: Additional data used to specify the position, for example
 * this is used in OneInchV5Evaluator to pass swap tx generated via 1inch API.
 */
struct PositionDescriptor {
    uint256 poolId;
    bytes extraData;
}

File 14 of 27 : Command.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {CommandLibrary} from "contracts/libraries/CommandLibrary.sol";

/**
 * @title Command
 * @notice Contains arguments for a low-level call.
 * @dev This struct allows deferring the call's execution, suspending it by passing it to another function or contract.
 * @dev `target` The address to be called.
 * @dev `value` Value to send in the call.
 * @dev `payload` Encoded call with function selector and arguments.
 */
struct Command {
    address target;
    uint256 value;
    bytes payload;
}

using CommandLibrary for Command global;

File 15 of 27 : IJitCompiler.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {IDecreasePositionEvaluator} from "./adapters/IDecreasePositionEvaluator.sol";
import {IExchangeEvaluator} from "./adapters/IExchangeEvaluator.sol";
import {IIncreasePositionEvaluator} from "./adapters/IIncreasePositionEvaluator.sol";
import {Command} from "./Command.sol";
import {Script} from "./Script.sol";

/**
 * @title IJitCompiler
 * @notice Compiles a script or an instruction into an array of [Commands](/interfaces/accountAbstraction/interpreter/Command.sol/struct.Command.html) using protocol evaluator matching `protocol` field in the underlying instruction.
 */
interface IJitCompiler {
    /**
     * @notice Instruction designed to increase:
     *  1. Caller's magnitude of the position determined by the [PositionDescriptor](../adapters/PositionDescriptor.sol/struct.PositionDescriptor.html).
     *  2. Callee's balance of token(s).
     * @notice and decrease:
     *  1. Caller's balance of token(s).
     *  2. (*Optional*) callee's supply of the position determined by the [PositionDescriptor](../adapters/PositionDescriptor.sol/struct.PositionDescriptor.html).
     * @dev This instruction will be evaluated in [IncreasePositionEvaluator](../adapters/IIncreasePositionEvaluator.sol/interface.IIncreasePositionEvaluator.html).
     * @dev `protocol` The name of the underlying protocol where instruction should be evaluated. For example: `curvefi`, `oneinchv5`
     * @dev `request` The [`IncreasePositionRequest`](../adapters/IIncreasePositionEvaluator.sol/interface.IIncreasePositionEvaluator.html#increasepositionrequest) containing all information required for instruction evaluation.
     */
    struct IncreasePositionInstruction {
        string protocol;
        IIncreasePositionEvaluator.IncreasePositionRequest request;
    }

    /**
     * @notice Instruction designed to increase:
     *  1. Caller's balance of token(s).
     *  2. (*Optional*) callee's supply of the position determined by the [PositionDescriptor](../adapters/PositionDescriptor.sol/struct.PositionDescriptor.html).
     * @notice and decrease:
     *  1. Caller's magnitude of the position determined by the [PositionDescriptor](../adapters/PositionDescriptor.sol/struct.PositionDescriptor.html).
     *  2. Callee's balance of token(s).
     * @dev This instruction will be evaluated in [DecreasePositionEvaluator](../adapters/IDecreasePositionEvaluator.sol/interface.IDecreasePositionEvaluator.html).
     * @dev `protocol` The name of the underlying protocol where instruction should be evaluated. For example: `curvefi`, `oneinchv5`
     * @dev `request` The [`DecreasePositionRequest`](../adapters/IDecreasePositionEvaluator.sol/interface.IDecreasePositionEvaluator.html#decreasepositionrequest) containing all information required for instruction evaluation.
     */
    struct DecreasePositionInstruction {
        string protocol;
        IDecreasePositionEvaluator.DecreasePositionRequest request;
    }

    /**
     * @notice Instruction designed to increase:
     *  1. (*Optional*) caller's balance of output token.
     *  2. Callee's balance of input token.
     * @notice and decrease:
     *  1. Caller's balance of input token.
     *  2. (*Optional*) callee's balance of output token.
     * @dev This instruction will be evaluated in [ExchangeEvaluator](../adapters/IExchangeEvaluator.sol/interface.IExchangeEvaluator.html).
     * @dev `protocol` The name of the underlying protocol where instruction should be evaluated. For example: `curvefi`, `oneinchv5`
     * @dev `request` The [`ExchangeRequest`](../adapters/IExchangeEvaluator.sol/interface.IExchangeEvaluator.html#exchangerequest) containing all information required for instruction evaluation.
     */
    struct ExchangeInstruction {
        string protocol;
        IExchangeEvaluator.ExchangeRequest request;
    }

    /**
     * @notice Instruction designed to increase:
     *  1. (*Optional*) caller's balance of output token.
     *  2. Callee's balance of input token.
     * @notice and decrease:
     *  1. Caller's balance of input token.
     *  2. (*Optional*) callee's balance of output token.
     * @dev This instruction will be evaluated in [ExchangeEvaluator](../adapters/IExchangeEvaluator.sol/interface.IExchangeEvaluator.html).
     * @dev **Important note:** this instruction has an identical structure to [ExchangeInstruction](#exchangeinstruction), but differs from it in that [ExchangeInstruction](#exchangeinstruction) is static and [ExchangeAllInstruction](#exchangeallinstruction) is dynamic. This means that the `amountIn` field will be set at runtime by the compiler to the caller's balance by the input token.
     * @dev `protocol` The name of the underlying protocol where instruction should be evaluated. For example: `curvefi`, `oneinchv5`
     * @dev `request` The [`ExchangeRequest`](../adapters/IExchangeEvaluator.sol/interface.IExchangeEvaluator.html#exchangerequest) containing all information required for instruction evaluation. The `amountIn` field will be set at runtime by the compiler to the caller's balance by the input token.
     */
    struct ExchangeAllInstruction {
        string protocol;
        IExchangeEvaluator.ExchangeRequest request;
    }

    /**
     * @notice Compiles a [Script](../Script.sol/struct.Script.html).
     * @dev **Important note:** don't put two instructions to the same script if one depend on the other because content of the script will be compiled at once meaning that balance changes will be applied only after the compilation of the entire script. If you have two instructions and one depends on the other, put them into different scripts.
     * @param script Script to compile
     * @return An array of [`Commands`](../Command.sol/struct.Command.html) to execute the instruction.
     */
    function compile(Script calldata script) external returns (Command[] memory);

    /**
     * @notice Compiles an increase position instruction.
     * @param instruction The [`IncreasePositionInstruction`](#increasepositioninstruction) struct.
     * @return An array of [`Commands`](../Command.sol/struct.Command.html) to execute the instruction.
     */
    function compileIncreasePositionInstruction(
        IncreasePositionInstruction calldata instruction
    ) external returns (Command[] memory);

    /**
     * @notice Compiles a decrease position instruction.
     * @param instruction The [`DecreasePositionInstruction`](#decreasepositioninstruction) struct.
     * @return An array of [`Commands`](../Command.sol/struct.Command.html) to execute the instruction.
     */
    function compileDecreasePositionInstruction(
        DecreasePositionInstruction calldata instruction
    ) external returns (Command[] memory);

    /**
     * @notice Compiles an exchange instruction.
     * @param instruction The [`ExchangeInstruction`](#exchangeinstruction) struct.
     * @return An array of [`Commands`](../Command.sol/struct.Command.html) to execute the instruction.
     */
    function compileExchangeInstruction(
        ExchangeInstruction calldata instruction
    ) external returns (Command[] memory);

    /**
     * @notice Sets the `amountIn` field to the balance of the caller by the input token and compiles an underlying exchange instruction.
     * @dev `amountIn` will be overriden with the balance of the caller by the input token.
     * @param instruction The [`ExchangeAllInstruction`](#exchangeallinstruction) struct.
     * @return An array of [`Commands`](../Command.sol/struct.Command.html) to execute the instruction.
     */
    function compileExchangeAllInstruction(
        ExchangeAllInstruction calldata instruction
    ) external returns (Command[] memory);
}

File 16 of 27 : Script.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

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

/**
 * @title Script
 * @notice A structure defining a script with various instructions for a JIT compiler.
 * @notice The [JIT compiler](../IJitCompiler.sol/interface.IJitCompiler.html) will compile it the following way:
 * 1. Flatten content's instructions arrays into a single-dimensional array: `flattened = [...increasePositionInstructions, ...decreasePositionInstructions, ...exchangeInstructions, ...exchangeAllInstructions]`.
 * 2. Execute `flattened[PC]` where `PC = sequence[i]` where `i` is the index of current loop iteration, starting from `i = 0`.
 * 3. Increment current loop interation index: `i = i + 1`.
 * 4. If `i < length(flattened)` go to step 2.
 * @dev `sequence` Auto-incrementing read-only program counter. Determines the order of execution of the instruction within the script.
 * @dev `increasePositionInstructions` An array of [`IncreasePositionInstructions`](../IJitCompiler.sol/interface.IJitCompiler.html#increasepositioninstruction).
 * @dev `decreasePositionInstructions` An array of [`DecreasePositionInstructions`](../IJitCompiler.sol/interface.IJitCompiler.html#decreasepositioninstruction).
 * @dev `exchangeInstructions` An array of [`ExchangeInstructions`](../IJitCompiler.sol/interface.IJitCompiler.html#exchangeinstruction).
 * @dev `exchangeAllInstructions` An array of [`ExchangeAllInstructions`](../IJitCompiler.sol/interface.IJitCompiler.html#exchangeallinstruction).
 */
struct Script {
    uint256[] sequence;
    IJitCompiler.IncreasePositionInstruction[] increasePositionInstructions;
    IJitCompiler.DecreasePositionInstruction[] decreasePositionInstructions;
    IJitCompiler.ExchangeInstruction[] exchangeInstructions;
    IJitCompiler.ExchangeAllInstruction[] exchangeAllInstructions;
}

File 17 of 27 : IAccount.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {State} from "contracts/base/StateMachine.sol";
import {Asset} from "contracts/libraries/AssetLibrary.sol";

import {Command} from "../interpreter/Command.sol";
import {Script} from "../interpreter/Script.sol";

interface IAccount {
    event AccountRegistered(address indexed owner, Asset[] collateral, Asset leverage);
    event LeverageSupplied(address indexed owner, uint256 supplied, int256 remaining);
    event AccountOpened(address indexed owner);
    event AccountSuspended(address indexed owner);
    event AccountClosed(address indexed previousOwner);

    /**
     * @notice Receive Ether and log the transaction.
     * @dev This function is called when the margin account receives Ether without a specific function call.
     */
    receive() external payable;

    /**
     * @notice After calling this function margin account will be in the registered state
     * Reverts if either not enough assets were transferred before the call
     * or the account is in an invalid state
     * @param user Address that will be assigned as an owner of the account
     * @param collateral Must be transfered to the account beforehand
     * @param requestedLeverage Asset that has to be supplied to the account
     * to switch it's state to the opened
     */
    function register(
        address user,
        Asset[] calldata collateral,
        Asset calldata requestedLeverage
    ) external payable;

    /**
     * @notice This method is a step of the margin account opening.
     * Once enough leverage supplied, margin account will be switched to
     * the opened state
     * @dev Reverts if either too much leverage is supplied
     * or account is in an invalid state
     * @param amount Amount to supply
     * @return allocationSuccess Whether enought leverage has been supplied
     * and margin account now ready to use by the owner
     */
    function supply(uint256 amount) external payable returns (bool allocationSuccess);

    /**
     * @notice Immediately switches the account to the suspended state and
     * tries to execute provided scripts
     * @dev Reverts if either script's compilation failed in [interpreter](/interfaces/accountAbstraction/interpreter/index.html)
     * or account's state cannot be switched to the suspended state
     * @param strategy If all scripts will be executed successfully
     * the account will be switched to the closed state
     * @return success Whether the account was switched to the closed state
     */
    function tryClose(Script[] calldata strategy) external returns (bool success);

    /**
     * @notice Allows margin account's owner to execute any low level call
     * @dev Reverts if either validation failed, call failed or account
     * is in an invalid state
     * @param cmd Parameters of the call: target, value and payload.
     * All the parameters will be validated in validation hub before execution.
     * All the approvals will performed automatically, owner does not need to
     * do it manually
     */
    function execute(Command calldata cmd) external;

    /**
     * @notice Returns the owner of the margin account.
     * @return The address of the the margin account owner.
     */
    function owner() external view returns (address);

    /**
     * @notice Leverage asset allocated (or being allocated) to the account
     */
    function leverage() external view returns (address, uint256);

    /**
     * @return currentState In which state the state machine is running now
     * @return switchTimestamp Time, when state was switched to the underlying state
     */
    function stateInfo() external view returns (State currentState, uint256 switchTimestamp);

    function allocationInfo()
        external
        view
        returns (
            State currentState,
            uint256 stateSwitchTimestamp,
            address owner,
            address leverageToken,
            uint256 leverageAmount
        );
}

File 18 of 27 : CommonErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

/**
 * @notice An error indicating that the amount for the specified token is zero.
 * @param token The address of the token with a zero amount.
 */
error AmountMustNotBeZero(address token);

/**
 * @notice An error indicating that an address must not be zero.
 */
error AddressMustNotBeZero();

/**
 * @notice An error indicating that an array must not be empty.
 */
error ArrayMustNotBeEmpty();

/**
 * @notice An error indicating storage is already up to date and doesn't need further processing.
 * @dev This error is thrown when attempting to update an entity(s) that is(are) already up to date.
 */
error AlreadyUpToDate();

/**
 * @notice An error indicating that an action is unauthorized for the specified account.
 * @param account The address of the unauthorized account.
 */
error UnauthorizedAccount(address account);

File 19 of 27 : IInitializable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

interface IInitializable {
    function initialize() external;
}

File 20 of 27 : Address.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

library Address {
    address internal constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    function set(bytes32 _slot, address _value) internal {
        assembly {
            sstore(_slot, _value)
        }
    }

    function get(bytes32 _slot) internal view returns (address result_) {
        assembly {
            result_ := sload(_slot)
        }
    }

    function isEth(address _token) internal pure returns (bool) {
        return _token == ETH || _token == address(0);
    }

    function sort(address _a, address _b) internal pure returns (address, address) {
        return _a < _b ? (_a, _b) : (_b, _a);
    }

    function sort(address[4] memory _array) internal pure returns (address[4] memory _sorted) {
        // Sorting network for the array of length 4
        (_sorted[0], _sorted[1]) = sort(_array[0], _array[1]);
        (_sorted[2], _sorted[3]) = sort(_array[2], _array[3]);

        (_sorted[0], _sorted[2]) = sort(_sorted[0], _sorted[2]);
        (_sorted[1], _sorted[3]) = sort(_sorted[1], _sorted[3]);
        (_sorted[1], _sorted[2]) = sort(_sorted[1], _sorted[2]);
    }
}

File 21 of 27 : AssetLibrary.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";

import {Asset} from "contracts/interfaces/accountAbstraction/compliance/Asset.sol";
import {AmountMustNotBeZero} from "contracts/interfaces/base/CommonErrors.sol";

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

library AssetLibrary {
    using SafeTransferLib for address;
    using Address for address;

    error NotEnoughReceived(address token, uint256 expected, uint256 received);

    function forward(Asset calldata _self, address _to) internal {
        if (_self.amount == 0) revert AmountMustNotBeZero(_self.token);

        if (_self.token.isEth()) _to.safeTransferETH(_self.amount);
        else _self.token.safeTransferFrom(msg.sender, _to, _self.amount);
    }

    function enforceReceived(Asset calldata _self) internal view {
        if (_self.amount == 0) revert AmountMustNotBeZero(_self.token);

        uint256 balance = _self.token.isEth()
            ? address(this).balance
            : _self.token.balanceOf(address(this));

        if (balance < _self.amount) revert NotEnoughReceived(_self.token, _self.amount, balance);
    }
}

File 22 of 27 : CalldataLibrary.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

library CalldataLibrary {
    uint256 private constant WORD_LENGTH = 32;

    function appendWord(
        bytes memory _payload,
        address _word
    ) internal pure returns (bytes memory result_) {
        result_ = bytes.concat(_payload, bytes12(0), bytes20(_word));
    }

    function extractLastWordAsAddress(bytes calldata _payload) internal pure returns (address) {
        uint256 start = _payload.length - WORD_LENGTH;
        uint256 end = start + WORD_LENGTH;

        bytes32 word = bytes32(_payload[start:end]);
        return address(uint160(uint256(word)));
    }
}

File 23 of 27 : CommandLibrary.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Command} from "contracts/interfaces/accountAbstraction/interpreter/Command.sol";
import {SafeCall} from "contracts/libraries/SafeCall.sol";

/**
 * @notice Utility to convert often-used methods into a Command object
 */
library CommandPresets {
    function approve(
        address _token,
        address _to,
        uint256 _amount
    ) internal pure returns (Command memory cmd_) {
        cmd_.target = _token;
        cmd_.payload = abi.encodeCall(IERC20.approve, (_to, _amount));
    }

    function transfer(
        address _token,
        address _to,
        uint256 _amount
    ) internal pure returns (Command memory cmd_) {
        cmd_.target = _token;
        cmd_.payload = abi.encodeCall(IERC20.transfer, (_to, _amount));
    }
}

library CommandExecutor {
    using SafeCall for Command[];

    function execute(Command[] calldata _cmds) external {
        _cmds.safeCallAll();
    }
}

library CommandLibrary {
    using CommandLibrary for Command[];

    function last(Command[] memory _self) internal pure returns (Command memory) {
        return _self[_self.length - 1];
    }

    function asArray(Command memory _self) internal pure returns (Command[] memory result_) {
        result_ = new Command[](1);
        result_[0] = _self;
    }

    function concat(
        Command memory _self,
        Command memory _cmd
    ) internal pure returns (Command[] memory result_) {
        result_ = new Command[](2);
        result_[0] = _self;
        result_[1] = _cmd;
    }

    function concat(
        Command memory _self,
        Command[] memory _cmds
    ) internal pure returns (Command[] memory result_) {
        result_ = new Command[](_cmds.length + 1);
        result_[0] = _self;
        for (uint256 i = 1; i < result_.length; i++) {
            result_[i] = _cmds[i - 1];
        }
    }

    function append(
        Command[] memory _self,
        Command[] memory _cmds
    ) internal pure returns (Command[] memory result_) {
        result_ = new Command[](_self.length + _cmds.length);
        uint256 i;
        for (; i < _self.length; i++) {
            result_[i] = _self[i];
        }
        for (; i < result_.length; i++) {
            result_[i] = _cmds[i - _self.length];
        }
    }

    function push(
        Command[] memory _self,
        Command memory _cmd
    ) internal pure returns (Command[] memory result_) {
        result_ = new Command[](_self.length + 1);
        for (uint256 i; i < _self.length; i++) {
            result_[i] = _self[i];
        }
        result_[_self.length] = _cmd;
    }

    function unshift(
        Command[] memory _self,
        Command memory _cmd
    ) internal pure returns (Command[] memory result_) {
        result_ = new Command[](1 + _self.length);
        result_[0] = _cmd;
        for (uint256 i = 1; i < result_.length; i++) {
            result_[i] = _self[i - 1];
        }
    }

    function unshift(
        Command[] memory _self,
        Command[] memory _cmds
    ) internal pure returns (Command[] memory result_) {
        result_ = new Command[](_cmds.length + _self.length);
        uint256 i;
        for (; i < _cmds.length; i++) {
            result_[i] = _cmds[i];
        }
        for (; i < result_.length; i++) {
            result_[i] = _self[i - _cmds.length];
        }
    }

    function populateWithApprove(
        Command memory _self,
        address _token,
        uint256 _amount
    ) internal pure returns (Command[] memory result_) {
        if (_amount != 0) {
            result_ = CommandPresets.approve(_token, _self.target, _amount).concat(_self);
        } else {
            result_ = _self.asArray();
        }
    }

    function populateWithRevokeAndApprove(
        Command memory _self,
        address _token,
        uint256 _amount
    ) internal pure returns (Command[] memory result_) {
        return
            CommandPresets.approve(_token, _self.target, 0).concat(
                _self.populateWithApprove(_token, _amount)
            );
    }

    function populateWithApprove(
        Command[] memory _self,
        address _token,
        uint256 _amount
    ) internal pure returns (Command[] memory result_) {
        if (_amount != 0) {
            result_ = _self.unshift(
                CommandPresets.approve(_token, _self[_self.length - 1].target, _amount)
            );
        } else {
            result_ = _self;
        }
    }

    function populateWithApprove(
        Command memory _self,
        address[2] memory _tokens,
        uint256[2] memory _amounts
    ) internal pure returns (Command[] memory result_) {
        if (_amounts[0] != 0 && _amounts[1] != 0) {
            result_ = CommandPresets
                .approve(_tokens[0], _self.target, _amounts[0])
                .concat(CommandPresets.approve(_tokens[1], _self.target, _amounts[1]))
                .push(_self);
        } else {
            if (_amounts[0] != 0) {
                result_ = populateWithApprove(_self, _tokens[0], _amounts[0]);
            } else {
                result_ = populateWithApprove(_self, _tokens[1], _amounts[1]);
            }
        }
    }

    function populateWithApprove(
        Command memory _self,
        address[3] memory _tokens,
        uint256[3] memory _amounts
    ) internal pure returns (Command[] memory result_) {
        if (_amounts[0] != 0 && _amounts[1] != 0 && _amounts[2] != 0) {
            result_ = CommandPresets
                .approve(_tokens[0], _self.target, _amounts[0])
                .concat(CommandPresets.approve(_tokens[1], _self.target, _amounts[1]))
                .push(CommandPresets.approve(_tokens[2], _self.target, _amounts[2]))
                .push(_self);
        } else {
            if (_amounts[0] == 0) {
                result_ = populateWithApprove(
                    _self,
                    [_tokens[1], _tokens[2]],
                    [_amounts[1], _amounts[2]]
                );
            } else if (_amounts[1] == 0) {
                result_ = populateWithApprove(
                    _self,
                    [_tokens[0], _tokens[2]],
                    [_amounts[0], _amounts[2]]
                );
            } else {
                result_ = populateWithApprove(
                    _self,
                    [_tokens[0], _tokens[1]],
                    [_amounts[0], _amounts[1]]
                );
            }
        }
    }

    function populateWithApprove(
        Command memory _self,
        address[4] memory _tokens,
        uint256[4] memory _amounts
    ) internal pure returns (Command[] memory result_) {
        if (_amounts[0] != 0 && _amounts[1] != 0 && _amounts[2] != 0 && _amounts[3] != 0) {
            result_ = CommandPresets
                .approve(_tokens[0], _self.target, _amounts[0])
                .concat(CommandPresets.approve(_tokens[1], _self.target, _amounts[1]))
                .push(CommandPresets.approve(_tokens[2], _self.target, _amounts[2]))
                .push(CommandPresets.approve(_tokens[3], _self.target, _amounts[3]))
                .push(_self);
        } else {
            if (_amounts[0] == 0) {
                result_ = populateWithApprove(
                    _self,
                    [_tokens[1], _tokens[2], _tokens[3]],
                    [_amounts[1], _amounts[2], _amounts[3]]
                );
            } else if (_amounts[1] == 0) {
                result_ = populateWithApprove(
                    _self,
                    [_tokens[0], _tokens[2], _tokens[3]],
                    [_amounts[0], _amounts[2], _amounts[3]]
                );
            } else if (_amounts[2] == 0) {
                result_ = populateWithApprove(
                    _self,
                    [_tokens[0], _tokens[1], _tokens[3]],
                    [_amounts[0], _amounts[1], _amounts[3]]
                );
            } else {
                result_ = populateWithApprove(
                    _self,
                    [_tokens[0], _tokens[1], _tokens[2]],
                    [_amounts[0], _amounts[1], _amounts[2]]
                );
            }
        }
    }
}

File 24 of 27 : SafeCall.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Command} from "contracts/interfaces/accountAbstraction/interpreter/Command.sol";

/**
 * @notice Safe methods performing a low-level calls that revert
 * if the call was not successful
 */
library SafeCall {
    using Address for address;

    function safeCallAll(Command[] memory _cmds) internal {
        for (uint256 i; i < _cmds.length; i++) {
            safeCall(_cmds[i]);
        }
    }

    function safeCall(Command memory _cmd) internal returns (bytes memory result_) {
        result_ = safeCall(_cmd.target, _cmd.value, _cmd.payload);
    }

    function safeCall(address _target, bytes memory _data) internal returns (bytes memory result_) {
        result_ = safeCall(_target, 0, _data);
    }

    function safeCall(
        address _target,
        uint256 _value,
        bytes memory _data
    ) internal returns (bytes memory result_) {
        result_ = _target.functionCallWithValue(_data, _value);
    }

    function safeDelegateCall(
        address _target,
        bytes memory _data
    ) internal returns (bytes memory result_) {
        result_ = _target.functionDelegateCall(_data);
    }
}

File 25 of 27 : LibBit.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  BIT TWIDDLING OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Find last set.
    /// Returns the index of the most significant bit of `x`,
    /// counting from the least significant bit position.
    /// If `x` is zero, returns 256.
    /// Equivalent to `log2(x)`, but without reverting for the zero case.
    function fls(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(8, iszero(x))

            r := or(r, shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))

            // For the remaining 32 bits, use a De Bruijn lookup.
            x := shr(r, x)
            x := or(x, shr(1, x))
            x := or(x, shr(2, x))
            x := or(x, shr(4, x))
            x := or(x, shr(8, x))
            x := or(x, shr(16, x))

            // forgefmt: disable-next-item
            r := or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))),
                0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f))
        }
    }

    /// @dev Count leading zeros.
    /// Returns the number of zeros preceding the most significant one bit.
    /// If `x` is zero, returns 256.
    function clz(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            let t := add(iszero(x), 255)

            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))

            // For the remaining 32 bits, use a De Bruijn lookup.
            x := shr(r, x)
            x := or(x, shr(1, x))
            x := or(x, shr(2, x))
            x := or(x, shr(4, x))
            x := or(x, shr(8, x))
            x := or(x, shr(16, x))

            // forgefmt: disable-next-item
            r := sub(t, or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))),
                0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f)))
        }
    }

    /// @dev Find first set.
    /// Returns the index of the least significant bit of `x`,
    /// counting from the least significant bit position.
    /// If `x` is zero, returns 256.
    /// Equivalent to `ctz` (count trailing zeros), which gives
    /// the number of zeros following the least significant one bit.
    function ffs(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(8, iszero(x))

            // Isolate the least significant bit.
            x := and(x, add(not(x), 1))

            r := or(r, shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))

            // For the remaining 32 bits, use a De Bruijn lookup.
            // forgefmt: disable-next-item
            r := or(r, byte(shr(251, mul(shr(r, x), shl(224, 0x077cb531))), 
                0x00011c021d0e18031e16140f191104081f1b0d17151310071a0c12060b050a09))
        }
    }

    /// @dev Returns the number of set bits in `x`.
    function popCount(uint256 x) internal pure returns (uint256 c) {
        /// @solidity memory-safe-assembly
        assembly {
            let max := not(0)
            let isMax := eq(x, max)
            x := sub(x, and(shr(1, x), div(max, 3)))
            x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
            x := and(add(x, shr(4, x)), div(max, 17))
            c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
        }
    }

    /// @dev Returns whether `x` is a power of 2.
    function isPo2(uint256 x) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `x && !(x & (x - 1))`.
            result := iszero(add(and(x, sub(x, 1)), iszero(x)))
        }
    }

    /// @dev Returns `x` reversed at the bit level.
    function reverseBits(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Computing masks on-the-fly reduces bytecode size by about 500 bytes.
            let m := not(0)
            r := x
            for { let s := 128 } 1 {} {
                m := xor(m, shl(s, m))
                r := or(and(shr(s, r), m), and(shl(s, r), not(m)))
                s := shr(1, s)
                if iszero(s) { break }
            }
        }
    }

    /// @dev Returns `x` reversed at the byte level.
    function reverseBytes(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Computing masks on-the-fly reduces bytecode size by about 200 bytes.
            let m := not(0)
            r := x
            for { let s := 128 } 1 {} {
                m := xor(m, shl(s, m))
                r := or(and(shr(s, r), m), and(shl(s, r), not(m)))
                s := shr(1, s)
                if eq(s, 4) { break }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     BOOLEAN OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `x & y`.
    function and(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := and(x, y)
        }
    }

    /// @dev Returns `x | y`.
    function or(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, y)
        }
    }

    /// @dev Returns a non-zero number if `b` is true, else 0.
    /// If `b` is from plain Solidity, the non-zero number will be 1.
    function toUint(bool b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := b
        }
    }
}

File 26 of 27 : LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The `length` of the output is too small to contain all the hex digits.
    error HexLengthInsufficient();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The constant returned when the `search` is not found in the string.
    uint256 internal constant NOT_FOUND = type(uint256).max;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     DECIMAL OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits.
            str := add(mload(0x40), 0x80)
            // Update the free memory pointer to allocate.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            let w := not(0) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                str := add(str, w) // `sub(str, 1)`.
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(int256 value) internal pure returns (string memory str) {
        if (value >= 0) {
            return toString(uint256(value));
        }
        unchecked {
            str = toString(uint256(-value));
        }
        /// @solidity memory-safe-assembly
        assembly {
            // We still have some spare memory space on the left,
            // as we have allocated 3 words (96 bytes) for up to 78 digits.
            let length := mload(str) // Load the string length.
            mstore(str, 0x2d) // Store the '-' character.
            str := sub(str, 1) // Move back the string pointer by a byte.
            mstore(str, add(length, 1)) // Update the string length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   HEXADECIMAL OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value, length);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexStringNoPrefix(uint256 value, uint256 length)
        internal
        pure
        returns (string memory str)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
            // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
            // We add 0x20 to the total and round down to a multiple of 0x20.
            // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
            str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let start := sub(str, add(length, length))
            let w := not(1) // Tsk.
            let temp := value
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for {} 1 {} {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(xor(str, start)) { break }
            }

            if temp {
                // Store the function selector of `HexLengthInsufficient()`.
                mstore(0x00, 0x2194895a)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2` bytes.
    function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
            str := add(mload(0x40), 0x80)
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let w := not(1) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(temp) { break }
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
    /// and the alphabets are capitalized conditionally according to
    /// https://eips.ethereum.org/EIPS/eip-55
    function toHexStringChecksummed(address value) internal pure returns (string memory str) {
        str = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(str, 0x22)
            let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
            let t := shl(240, 136) // `0b10001000 << 240`
            for { let i := 0 } 1 {} {
                mstore(add(i, i), mul(t, byte(i, hashed)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
            mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
            o := add(o, 0x20)
            mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(address value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            str := mload(0x40)

            // Allocate the memory.
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
            mstore(0x40, add(str, 0x80))

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            str := add(str, 2)
            mstore(str, 40)

            let o := add(str, 0x20)
            mstore(add(o, 40), 0)

            value := shl(96, value)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let i := 0 } 1 {} {
                let p := add(o, add(i, i))
                let temp := byte(i, value)
                mstore8(add(p, 1), mload(and(temp, 15)))
                mstore8(p, mload(shr(4, temp)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexString(bytes memory raw) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(raw);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(raw)
            str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
            mstore(str, add(length, length)) // Store the length of the output.

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let o := add(str, 0x20)
            let end := add(raw, length)

            for {} iszero(eq(raw, end)) {} {
                raw := add(raw, 1)
                mstore8(add(o, 1), mload(and(mload(raw), 15)))
                mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                o := add(o, 2)
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate the memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RUNE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the number of UTF characters in the string.
    function runeCount(string memory s) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                mstore(0x00, div(not(0), 255))
                mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for { result := 1 } 1 { result := add(result, 1) } {
                    o := add(o, byte(0, mload(shr(250, mload(o)))))
                    if iszero(lt(o, end)) { break }
                }
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string.
    /// (i.e. all characters codes are in [0..127])
    function is7BitASCII(string memory s) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(7, div(not(0), 255))
            result := 1
            let n := mload(s)
            if n {
                let o := add(s, 0x20)
                let end := add(o, n)
                let last := mload(end)
                mstore(end, 0)
                for {} 1 {} {
                    if and(mask, mload(o)) {
                        result := 0
                        break
                    }
                    o := add(o, 0x20)
                    if iszero(lt(o, end)) { break }
                }
                mstore(end, last)
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   BYTE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance and bytecode compactness, all indices of the following operations
    // are byte (ASCII) offsets, not UTF character offsets.

    /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
    function replace(string memory subject, string memory search, string memory replacement)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)
            let replacementLength := mload(replacement)

            subject := add(subject, 0x20)
            search := add(search, 0x20)
            replacement := add(replacement, 0x20)
            result := add(mload(0x40), 0x20)

            let subjectEnd := add(subject, subjectLength)
            if iszero(gt(searchLength, subjectLength)) {
                let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        for { let o := 0 } 1 {} {
                            mstore(add(result, o), mload(add(replacement, o)))
                            o := add(o, 0x20)
                            if iszero(lt(o, replacementLength)) { break }
                        }
                        result := add(result, replacementLength)
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(result, t)
                    result := add(result, 1)
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
            }

            let resultRemainder := result
            result := add(mload(0x40), 0x20)
            let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
            // Copy the rest of the string one word at a time.
            for {} lt(subject, subjectEnd) {} {
                mstore(resultRemainder, mload(subject))
                resultRemainder := add(resultRemainder, 0x20)
                subject := add(subject, 0x20)
            }
            result := sub(result, 0x20)
            let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
            mstore(last, 0)
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
            mstore(result, k) // Store the length.
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let subjectLength := mload(subject) } 1 {} {
                if iszero(mload(search)) {
                    if iszero(gt(from, subjectLength)) {
                        result := from
                        break
                    }
                    result := subjectLength
                    break
                }
                let searchLength := mload(search)
                let subjectStart := add(subject, 0x20)

                result := not(0) // Initialize to `NOT_FOUND`.

                subject := add(subjectStart, from)
                let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)

                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(add(search, 0x20))

                if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }

                if iszero(lt(searchLength, 0x20)) {
                    for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, searchLength), h) {
                                result := sub(subject, subjectStart)
                                break
                            }
                        }
                        subject := add(subject, 1)
                        if iszero(lt(subject, end)) { break }
                    }
                    break
                }
                for {} 1 {} {
                    if iszero(shr(m, xor(mload(subject), s))) {
                        result := sub(subject, subjectStart)
                        break
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = indexOf(subject, search, 0);
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                result := not(0) // Initialize to `NOT_FOUND`.
                let searchLength := mload(search)
                if gt(searchLength, mload(subject)) { break }
                let w := result

                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) { from := fromMax }

                let end := add(add(subject, 0x20), w)
                subject := add(add(subject, 0x20), from)
                if iszero(gt(subject, end)) { break }
                // As this function is not too often used,
                // we shall simply use keccak256 for smaller bytecode size.
                for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                    if eq(keccak256(subject, searchLength), h) {
                        result := sub(subject, add(end, 1))
                        break
                    }
                    subject := add(subject, w) // `sub(subject, 1)`.
                    if iszero(gt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = lastIndexOf(subject, search, uint256(int256(-1)));
    }

    /// @dev Returns whether `subject` starts with `search`.
    function startsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                iszero(gt(searchLength, mload(subject))),
                eq(
                    keccak256(add(subject, 0x20), searchLength),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns whether `subject` ends with `search`.
    function endsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            let subjectLength := mload(subject)
            // Whether `search` is not longer than `subject`.
            let withinRange := iszero(gt(searchLength, subjectLength))
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                withinRange,
                eq(
                    keccak256(
                        // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                        add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                        searchLength
                    ),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLength))) {
                subject := add(subject, 0x20)
                result := mload(0x40)
                let output := add(result, 0x20)
                for {} 1 {} {
                    // Copy the `subject` one word at a time.
                    for { let o := 0 } 1 {} {
                        mstore(add(output, o), mload(add(subject, o)))
                        o := add(o, 0x20)
                        if iszero(lt(o, subjectLength)) { break }
                    }
                    output := add(output, subjectLength)
                    times := sub(times, 1)
                    if iszero(times) { break }
                }
                mstore(output, 0) // Zeroize the slot after the string.
                let resultLength := sub(output, add(result, 0x20))
                mstore(result, resultLength) // Store the length.
                // Allocate the memory.
                mstore(0x40, add(result, add(resultLength, 0x20)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(string memory subject, uint256 start, uint256 end)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(gt(subjectLength, end)) { end := subjectLength }
            if iszero(gt(subjectLength, start)) { start := subjectLength }
            if lt(start, end) {
                result := mload(0x40)
                let resultLength := sub(end, start)
                mstore(result, resultLength)
                subject := add(subject, start)
                let w := not(0x1f)
                // Copy the `subject` one word at a time, backwards.
                for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
                    mstore(add(result, o), mload(add(subject, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) { break }
                }
                // Zeroize the slot after the string.
                mstore(add(add(result, 0x20), resultLength), 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(string memory subject, uint256 start)
        internal
        pure
        returns (string memory result)
    {
        result = slice(subject, start, uint256(int256(-1)));
    }

    /// @dev Returns all the indices of `search` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256[] memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)

            if iszero(gt(searchLength, subjectLength)) {
                subject := add(subject, 0x20)
                search := add(search, 0x20)
                result := add(mload(0x40), 0x20)

                let subjectStart := subject
                let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Append to `result`.
                        mstore(result, sub(subject, subjectStart))
                        result := add(result, 0x20)
                        // Advance `subject` by `searchLength`.
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                let resultEnd := result
                // Assign `result` to the free memory pointer.
                result := mload(0x40)
                // Store the length of `result`.
                mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(resultEnd, 0x20))
            }
        }
    }

    /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(string memory subject, string memory delimiter)
        internal
        pure
        returns (string[] memory result)
    {
        uint256[] memory indices = indicesOf(subject, delimiter);
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            let indexPtr := add(indices, 0x20)
            let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
            mstore(add(indicesEnd, w), mload(subject))
            mstore(indices, add(mload(indices), 1))
            let prevIndex := 0
            for {} 1 {} {
                let index := mload(indexPtr)
                mstore(indexPtr, 0x60)
                if iszero(eq(index, prevIndex)) {
                    let element := mload(0x40)
                    let elementLength := sub(index, prevIndex)
                    mstore(element, elementLength)
                    // Copy the `subject` one word at a time, backwards.
                    for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
                        mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                        o := add(o, w) // `sub(o, 0x20)`.
                        if iszero(o) { break }
                    }
                    // Zeroize the slot after the string.
                    mstore(add(add(element, 0x20), elementLength), 0)
                    // Allocate memory for the length and the bytes,
                    // rounded up to a multiple of 32.
                    mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
                    // Store the `element` into the array.
                    mstore(indexPtr, element)
                }
                prevIndex := add(index, mload(delimiter))
                indexPtr := add(indexPtr, 0x20)
                if iszero(lt(indexPtr, indicesEnd)) { break }
            }
            result := indices
            if iszero(mload(delimiter)) {
                result := add(indices, 0x20)
                mstore(result, sub(mload(indices), 2))
            }
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(string memory a, string memory b)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            result := mload(0x40)
            let aLength := mload(a)
            // Copy `a` one word at a time, backwards.
            for { let o := and(add(mload(a), 0x20), w) } 1 {} {
                mstore(add(result, o), mload(add(a, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let bLength := mload(b)
            let output := add(result, mload(a))
            // Copy `b` one word at a time, backwards.
            for { let o := and(add(bLength, 0x20), w) } 1 {} {
                mstore(add(output, o), mload(add(b, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let totalLength := add(aLength, bLength)
            let last := add(add(result, 0x20), totalLength)
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Stores the length.
            mstore(result, totalLength)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 0x1f), w))
        }
    }

    /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function toCase(string memory subject, bool toUpper)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(subject)
            if length {
                result := add(mload(0x40), 0x20)
                subject := add(subject, 1)
                let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                let w := not(0)
                for { let o := length } 1 {} {
                    o := add(o, w)
                    let b := and(0xff, mload(add(subject, o)))
                    mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                    if iszero(o) { break }
                }
                result := mload(0x40)
                mstore(result, length) // Store the length.
                let last := add(add(result, 0x20), length)
                mstore(last, 0) // Zeroize the slot after the string.
                mstore(0x40, add(last, 0x20)) // Allocate the memory.
            }
        }
    }

    /// @dev Returns a lowercased copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function lower(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, false);
    }

    /// @dev Returns an UPPERCASED copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function upper(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, true);
    }

    /// @dev Escapes the string to be used within HTML tags.
    function escapeHTML(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {
                let end := add(s, mload(s))
                result := add(mload(0x40), 0x20)
                // Store the bytes of the packed offsets and strides into the scratch space.
                // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
                mstore(0x1f, 0x900094)
                mstore(0x08, 0xc0000000a6ab)
                // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
                mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
            } iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // Not in `["\"","'","&","<",">"]`.
                if iszero(and(shl(c, 1), 0x500000c400000000)) {
                    mstore8(result, c)
                    result := add(result, 1)
                    continue
                }
                let t := shr(248, mload(c))
                mstore(result, mload(and(t, 0x1f)))
                result := add(result, shr(5, t))
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    function escapeJSON(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {
                let end := add(s, mload(s))
                result := add(mload(0x40), 0x20)
                // Store "\\u0000" in scratch space.
                // Store "0123456789abcdef" in scratch space.
                // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
                // into the scratch space.
                mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
                // Bitmask for detecting `["\"","\\"]`.
                let e := or(shl(0x22, 1), shl(0x5c, 1))
            } iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                if iszero(lt(c, 0x20)) {
                    if iszero(and(shl(c, 1), e)) {
                        // Not in `["\"","\\"]`.
                        mstore8(result, c)
                        result := add(result, 1)
                        continue
                    }
                    mstore8(result, 0x5c) // "\\".
                    mstore8(add(result, 1), c)
                    result := add(result, 2)
                    continue
                }
                if iszero(and(shl(c, 1), 0x3700)) {
                    // Not in `["\b","\t","\n","\f","\d"]`.
                    mstore8(0x1d, mload(shr(4, c))) // Hex value.
                    mstore8(0x1e, mload(and(c, 15))) // Hex value.
                    mstore(result, mload(0x19)) // "\\u00XX".
                    result := add(result, 6)
                    continue
                }
                mstore8(result, 0x5c) // "\\".
                mstore8(add(result, 1), mload(add(c, 8)))
                result := add(result, 2)
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Returns whether `a` equals `b`.
    function eq(string memory a, string memory b) internal pure returns (bool result) {
        assembly {
            result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
        }
    }

    /// @dev Packs a single string with its length into a single word.
    /// Returns `bytes32(0)` if the length is zero or greater than 31.
    function packOne(string memory a) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // We don't need to zero right pad the string,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    // Load the length and the bytes.
                    mload(add(a, 0x1f)),
                    // `length != 0 && length < 32`. Abuses underflow.
                    // Assumes that the length is valid and within the block gas limit.
                    lt(sub(mload(a), 1), 0x1f)
                )
        }
    }

    /// @dev Unpacks a string packed using {packOne}.
    /// Returns the empty string if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packOne}, the output behaviour is undefined.
    function unpackOne(bytes32 packed) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            result := mload(0x40)
            // Allocate 2 words (1 for the length, 1 for the bytes).
            mstore(0x40, add(result, 0x40))
            // Zeroize the length slot.
            mstore(result, 0)
            // Store the length and bytes.
            mstore(add(result, 0x1f), packed)
            // Right pad with zeroes.
            mstore(add(add(result, 0x20), mload(result)), 0)
        }
    }

    /// @dev Packs two strings with their lengths into a single word.
    /// Returns `bytes32(0)` if combined length is zero or greater than 30.
    function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let aLength := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    // Load the length and the bytes of `a` and `b`.
                    or(
                        shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
                        mload(sub(add(b, 0x1e), aLength))
                    ),
                    // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                    // Assumes that the lengths are valid and within the block gas limit.
                    lt(sub(add(aLength, mload(b)), 1), 0x1e)
                )
        }
    }

    /// @dev Unpacks strings packed using {packTwo}.
    /// Returns the empty strings if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packTwo}, the output behaviour is undefined.
    function unpackTwo(bytes32 packed)
        internal
        pure
        returns (string memory resultA, string memory resultB)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            resultA := mload(0x40)
            resultB := add(resultA, 0x40)
            // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
            mstore(0x40, add(resultB, 0x40))
            // Zeroize the length slots.
            mstore(resultA, 0)
            mstore(resultB, 0)
            // Store the lengths and bytes.
            mstore(add(resultA, 0x1f), packed)
            mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
            // Right pad with zeroes.
            mstore(add(add(resultA, 0x20), mload(resultA)), 0)
            mstore(add(add(resultB, 0x20), mload(resultB)), 0)
        }
    }

    /// @dev Directly returns `a` without copying.
    function directReturn(string memory a) internal pure {
        assembly {
            // Assumes that the string does not start from the scratch space.
            let retStart := sub(a, 0x20)
            let retSize := add(mload(a), 0x40)
            // Right pad with zeroes. Just in case the string is produced
            // by a method that doesn't zero right pad.
            mstore(add(retStart, retSize), 0)
            // Store the return offset.
            mstore(retStart, 0x20)
            // End the transaction, returning the string.
            return(retStart, retSize)
        }
    }
}

File 27 of 27 : SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH
    /// that disallows any storage writes.
    uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    /// Multiply by a small constant (e.g. 2), if needed.
    uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` (in wei) ETH to `to`.
    /// Reverts upon failure.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                if iszero(create(amount, 0x0b, 0x16)) {
                    // For better gas estimation.
                    if iszero(gt(gas(), 1000000)) { revert(0, 0) }
                }
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend
    /// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default
    /// for 99% of cases and can be overriden with the three-argument version of this
    /// function if necessary.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        // Manually inlined because the compiler doesn't inline functions with branches.
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                if iszero(create(amount, 0x0b, 0x16)) {
                    // For better gas estimation.
                    if iszero(gt(gas(), 1000000)) { revert(0, 0) }
                }
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend.
    ///
    /// Note: Does NOT revert upon failure.
    /// Returns whether the transfer of ETH is successful instead.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            success := call(gasStipend, to, amount, 0, 0, 0, 0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x0c, 0x23b872dd000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            // Store the function selector of `balanceOf(address)`.
            mstore(0x0c, 0x70a08231000000000000000000000000)
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x00, 0x23b872dd)
            // The `amount` argument is already written to the memory word at 0x6c.
            amount := mload(0x60)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            // Store the function selector of `transfer(address,uint256)`.
            mstore(0x00, 0xa9059cbb000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x14, to) // Store the `to` argument.
            // The `amount` argument is already written to the memory word at 0x34.
            amount := mload(0x34)
            // Store the function selector of `transfer(address,uint256)`.
            mstore(0x00, 0xa9059cbb000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            // Store the function selector of `approve(address,uint256)`.
            mstore(0x00, 0x095ea7b3000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `ApproveFailed()`.
                mstore(0x00, 0x3e3f8f73)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            // Store the function selector of `balanceOf(address)`.
            mstore(0x00, 0x70a08231000000000000000000000000)
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

Settings
{
  "viaIR": false,
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {
    "contracts/libraries/CommandLibrary.sol": {
      "CommandExecutor": "0x776197c47823dd8a2dd318d81a0556a9e4fb3b95"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_marginEngine","type":"address"},{"internalType":"address","name":"_compliance","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":"AddressMustNotBeZero","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"AmountMustNotBeZero","type":"error"},{"inputs":[],"name":"ArrayMustNotBeEmpty","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"HostedTransitionMustBeCalledFromSelf","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"IdIsReservedForUndefinedState","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"State","name":"","type":"uint256"}],"name":"InvalidState","type":"error"},{"inputs":[],"name":"NewOwnerMustBeNonZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"received","type":"uint256"}],"name":"NotEnoughReceived","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OwnerDoesNotExist","type":"error"},{"inputs":[{"internalType":"State","name":"from","type":"uint256"},{"internalType":"State","name":"to","type":"uint256"}],"name":"TransitionAlreadyExists","type":"error"},{"inputs":[{"internalType":"State","name":"from","type":"uint256"},{"internalType":"State","name":"to","type":"uint256"}],"name":"TransitionDoesNotExist","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"UnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"State","name":"expectedStatesBitmap","type":"uint256"},{"internalType":"State","name":"currentState","type":"uint256"}],"name":"UnexpectedState","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"}],"name":"AccountClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"AccountOpened","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct Asset[]","name":"collateral","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct Asset","name":"leverage","type":"tuple"}],"name":"AccountRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"AccountSuspended","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"supplied","type":"uint256"},{"indexed":false,"internalType":"int256","name":"remaining","type":"int256"}],"name":"LeverageSupplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerHasBeenAssigned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"}],"name":"OwnerHasBeenUnassigned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"State","name":"from","type":"uint256"},{"indexed":false,"internalType":"State","name":"to","type":"uint256"}],"name":"StateChanged","type":"event"},{"inputs":[],"name":"allocationInfo","outputs":[{"internalType":"State","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"closeAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"compiler","outputs":[{"internalType":"contract IJitCompiler","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"compliance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct Command","name":"_cmd","type":"tuple"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"leverage","outputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marginEngine","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"openAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Asset[]","name":"_collateral","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Asset","name":"_leverage","type":"tuple"}],"name":"register","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_args","type":"bytes"}],"name":"registerAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stateInfo","outputs":[{"internalType":"State","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"supply","outputs":[{"internalType":"bool","name":"allocationSuccess_","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"suspendAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"suspendUnopenedAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256[]","name":"sequence","type":"uint256[]"},{"components":[{"internalType":"string","name":"protocol","type":"string"},{"components":[{"components":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct PositionDescriptor","name":"descriptor","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Asset[]","name":"input","type":"tuple[]"},{"internalType":"uint256","name":"minLiquidityOut","type":"uint256"}],"internalType":"struct IIncreasePositionEvaluator.IncreasePositionRequest","name":"request","type":"tuple"}],"internalType":"struct IJitCompiler.IncreasePositionInstruction[]","name":"increasePositionInstructions","type":"tuple[]"},{"components":[{"internalType":"string","name":"protocol","type":"string"},{"components":[{"components":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct PositionDescriptor","name":"descriptor","type":"tuple"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Asset[]","name":"minOutput","type":"tuple[]"}],"internalType":"struct IDecreasePositionEvaluator.DecreasePositionRequest","name":"request","type":"tuple"}],"internalType":"struct IJitCompiler.DecreasePositionInstruction[]","name":"decreasePositionInstructions","type":"tuple[]"},{"components":[{"internalType":"string","name":"protocol","type":"string"},{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct IExchangeEvaluator.ExchangeRequest","name":"request","type":"tuple"}],"internalType":"struct IJitCompiler.ExchangeInstruction[]","name":"exchangeInstructions","type":"tuple[]"},{"components":[{"internalType":"string","name":"protocol","type":"string"},{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct IExchangeEvaluator.ExchangeRequest","name":"request","type":"tuple"}],"internalType":"struct IJitCompiler.ExchangeAllInstruction[]","name":"exchangeAllInstructions","type":"tuple[]"}],"internalType":"struct Script[]","name":"strategy","type":"tuple[]"}],"name":"tryClose","outputs":[{"internalType":"bool","name":"success_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

610180604052600160ff1b60805262000019600062000101565b6101005262000029600162000101565b6101205262000039600262000101565b6101405262000049600362000101565b610160523480156200005a57600080fd5b506040516200336c3803806200336c8339810160408190526200007d916200015c565b806001600160a01b038116620000a6576040516302f4924160e51b815260040160405180910390fd5b6001600160a01b0390811660a05282161580620000ca57506001600160a01b038116155b15620000e9576040516302f4924160e51b815260040160405180910390fd5b6001600160a01b0391821660c0521660e05262000194565b600060fe1960ff831601620001325760405163a57c436b60e01b815260ff8316600482015260240160405180910390fd5b600160ff83161b92915050565b80516001600160a01b03811681146200015757600080fd5b919050565b600080604083850312156200017057600080fd5b6200017b836200013f565b91506200018b602084016200013f565b90509250929050565b60805160a05160c05160e051610100516101205161014051610160516130bc620002b060003960008181610632015281816106d10152818161079f01528181610d4601528181610d960152610dc50152600081816105f4015281816106f20152818161077201528181610ae101528181610cf60152610d7501526000818161049201528181610c5601528181610ca601528181610cd501528181610d250152610f8b01526000818161094b01528181610c8501528181610de60152610ef901526000818161035801526107d201526000818161012f01528181610432015281816106760152610e9e0152600081816102b901526118c4015260008181610c3501528181610f1a015281816111d7015261195e01526130bc6000f3fe6080604052600436106101115760003560e01c80636290865d116100a55780638da5cb5b11610074578063c1611f6211610059578063c1611f62146103a2578063ec8accbc146103c2578063f966aac5146103e257600080fd5b80638da5cb5b1461037a57806390631d8e1461038f57600080fd5b80636290865d146102a757806377e747ca146102db5780638129fc1c14610331578063866517e81461034657600080fd5b80633db0a3e4116100e15780633db0a3e414610225578063506450e91461024557806356a5c6e8146102675780635c1c6dcd1461028757600080fd5b80624006e01461011d5780632c86d98e1461017b5780632cd16a8a146101d8578063354030231461020257600080fd5b3661011857005b600080fd5b34801561012957600080fd5b506101517f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561018757600080fd5b506000546001546101ac9173ffffffffffffffffffffffffffffffffffffffff169082565b6040805173ffffffffffffffffffffffffffffffffffffffff9093168352602083019190915201610172565b3480156101e457600080fd5b506101ed610402565b60408051928352602083019190915201610172565b610215610210366004611fd3565b610418565b6040519015158152602001610172565b34801561023157600080fd5b50610215610240366004611fec565b61065c565b34801561025157600080fd5b50610265610260366004612061565b61097b565b005b34801561027357600080fd5b50610265610282366004612061565b610a10565b34801561029357600080fd5b506102656102a23660046120c1565b610ad7565b3480156102b357600080fd5b506101517f000000000000000000000000000000000000000000000000000000000000000081565b3480156102e757600080fd5b506102f0610b25565b60408051958652602086019490945273ffffffffffffffffffffffffffffffffffffffff92831693850193909352166060830152608082015260a001610172565b34801561033d57600080fd5b50610265610b6d565b34801561035257600080fd5b506101517f000000000000000000000000000000000000000000000000000000000000000081565b34801561038657600080fd5b50610151610e77565b61026561039d36600461211e565b610e86565b3480156103ae57600080fd5b506102656103bd366004612061565b610fdc565b3480156103ce57600080fd5b506102656103dd366004612061565b611071565b3480156103ee57600080fd5b506102656103fd366004612061565b611163565b60008061040d6111ad565b600354915091509091565b60003373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610490576040517f32b2baa30000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006104c36104bc6111ad565b82906111fc565b61050b57806104d06111ad565b6040517f8e653d8c00000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401610487565b82600003610561576000546040517f3b9b86ec00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610487565b8260026000828254610573919061220e565b909155506105819050610e77565b73ffffffffffffffffffffffffffffffffffffffff167fd81dc1ab773745d6113d7b56db63d143b38c598946eaf907995eeba71ee28799846002546000600101546105cc9190612221565b6040805192835260208301919091520160405180910390a260015460025403610621576106187f0000000000000000000000000000000000000000000000000000000000000000611208565b60019150610656565b6001546002541115610656576106567f0000000000000000000000000000000000000000000000000000000000000000611208565b50919050565b60003373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146106cf576040517f32b2baa3000000000000000000000000000000000000000000000000000000008152336004820152602401610487565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000001761071d6104bc6111ad565b61072a57806104d06111ad565b6000839003610765576040517fe1d362ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107956107706111ad565b7f00000000000000000000000000000000000000000000000000000000000000001490565b156107c3576107c37f0000000000000000000000000000000000000000000000000000000000000000611208565b60005b838110156109455760007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16634763f39d87878581811061081e5761081e612241565b90506020028101906108309190612270565b6040518263ffffffff1660e01b815260040161084c9190612864565b6000604051808303816000875af115801561086b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526108b19190810190612a69565b6040517fd670be0c00000000000000000000000000000000000000000000000000000000815290915073776197c47823dd8a2dd318d81a0556a9e4fb3b959063d670be0c90610904908490600401612c30565b60006040518083038186803b15801561091c57600080fd5b505af492505050801561092d575060015b61093c57600093505050610974565b506001016107c6565b5061096f7f0000000000000000000000000000000000000000000000000000000000000000611208565b600191505b5092915050565b3330146109b4576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109bd42600355565b60006002556109ca610e77565b73ffffffffffffffffffffffffffffffffffffffff167f89e8e0cb75caa917b6632d3053b50971462790ea99d9fe21b4b27ce71017515660405160405180910390a25050565b333014610a49576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5242600355565b6000610a5c610e77565b9050610a66611224565b50600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155600181905560405173ffffffffffffffffffffffffffffffffffffffff8316917fa29911196d428d7968f8bde7515181a391bfa16e26042f789f3f2da7665e25de91a2505050565b610adf6112ec565b7f0000000000000000000000000000000000000000000000000000000000000000610b0b6104bc6111ad565b610b1857806104d06111ad565b610b218261135c565b5050565b6000806000806000610b356111ad565b600354610b40610e77565b600054600154939992985090965073ffffffffffffffffffffffffffffffffffffffff1694509092509050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0080546002919068010000000000000000900460ff1680610bbc5750805467ffffffffffffffff808416911610155b15610bf3576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001667ffffffffffffffff83161768010000000000000000178155610c807f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000003063ec8accbc61136e565b610cd07f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000003063ec8accbc61136e565b610d207f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000003063c1611f6261136e565b610d707f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000003063506450e961136e565b610dc07f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000003063f966aac561136e565b610e107f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000306356a5c6e861136e565b80547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16815560405167ffffffffffffffff831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050565b6000610e81611472565b905090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610ef7576040517f32b2baa3000000000000000000000000000000000000000000000000000000008152336004820152602401610487565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000017610f456104bc6111ad565b610f5257806104d06111ad565b60005b83811015610f8557610f7d858583818110610f7257610f72612241565b90506040020161149c565b600101610f55565b50610fd57f000000000000000000000000000000000000000000000000000000000000000086868686604051602001610fc19493929190612ce5565b6040516020818303038152906040526115dc565b5050505050565b333014611015576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61101e42600355565b600060025561102b610e77565b73ffffffffffffffffffffffffffffffffffffffff167f2077f6e031a8faa17b96131641b087822c10522fe1a94841987eccbe8f619f7f60405160405180910390a25050565b3330146110aa576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110b342600355565b600080806110c384860186612da5565b9250925092506110d2836117d3565b508051600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9283161790556020820151600155604051908416907f5f0651ae754e97c1f9b53e213b4ac64372e4f4441d6e6851b17bd129b4426076906111549085908590612e6c565b60405180910390a25050505050565b33301461119c576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111a542600355565b6109ca610e77565b7fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30d54806111f957507f000000000000000000000000000000000000000000000000000000000000000090565b90565b81811615155b92915050565b61122181604051806020016040528060008152506115dc565b50565b60008061122f611472565b91508173ffffffffffffffffffffffffffffffffffffffff160361127f576040517fbd704aae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112a860007f608a8c8eefc529e0455c0b7a17a3c2a1a40a7f0cd5045e3b46a4aa8ae853476d55565b60405173ffffffffffffffffffffffffffffffffffffffff8216907f870217a7bae08cb1e27777025a86e87ce4df51b0e2c6cc144ed2976e78a5c79a90600090a290565b6112f4611472565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461135a576040517f32b2baa3000000000000000000000000000000000000000000000000000000008152336004820152602401610487565b565b61122181611369836118b3565b611922565b600061137a858561195a565b60008181527fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30e6020526040902054909150156113ec576040517fb57df58c0000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610487565b60009081527fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30e6020908152604090912080547fffffffffffffffff0000000000000000000000000000000000000000000000001663ffffffff939093169390911b77ffffffffffffffffffffffffffffffffffffffff0000000016929092171790555050565b6000610e817f608a8c8eefc529e0455c0b7a17a3c2a1a40a7f0cd5045e3b46a4aa8ae853476d5490565b8060200135600003611500576114b56020820182612f04565b6040517f3b9b86ec00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610487565b600061152d6115126020840184612f04565b73ffffffffffffffffffffffffffffffffffffffff16611a3b565b6115635761155e306115426020850185612f04565b73ffffffffffffffffffffffffffffffffffffffff1690611a8b565b611565565b475b90508160200135811015610b21576115806020830183612f04565b6040517f43bb149000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526020830135602482015260448101829052606401610487565b6115e582611ac0565b61161e576040517f815506a800000000000000000000000000000000000000000000000000000000815260048101839052602401610487565b600061163161162b6111ad565b8461195a565b60008181527fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30e60205260409020549091506116ab5761166e6111ad565b6040517f8d157bec000000000000000000000000000000000000000000000000000000008152600481019190915260248101849052604401610487565b7f1c0c6794b8c626515ef0eaa59cf29f712f686771b359ff1a5a05738b92d054846116d46111ad565b60408051918252602082018690520160405180910390a17fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30d83905560008181527fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30e602090815260409182902054915160e083901b7fffffffff000000000000000000000000000000000000000000000000000000001681529082901c73ffffffffffffffffffffffffffffffffffffffff169163ffffffff169061179c908590600401612f21565b600060405180830381600087803b1580156117b657600080fd5b505af11580156117ca573d6000803e3d6000fd5b50505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8216611822576040517f55e7da0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61182a611472565b9050611854827f608a8c8eefc529e0455c0b7a17a3c2a1a40a7f0cd5045e3b46a4aa8ae853476d55565b8173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f76122449b9f9900601b55986d5b53d8d3f84b676f9d216a64334a914d21accd160405160405180910390a3919050565b6060600060405180606001604052807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1681526020016000815260200161190e85611b15565b9052905061191b81611bc1565b9392505050565b6000818060200190518101906119389190612a69565b9050826020013561194882611bda565b6020015261195581611c24565b505050565b60007f00000000000000000000000000000000000000000000000000000000000000008314158015611992575061199083611ac0565b155b156119cc576040517f815506a800000000000000000000000000000000000000000000000000000000815260048101849052602401610487565b6119d582611ac0565b611a0e576040517f815506a800000000000000000000000000000000000000000000000000000000815260048101839052602401610487565b50604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b600073ffffffffffffffffffffffffffffffffffffffff821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148061120257505073ffffffffffffffffffffffffffffffffffffffff161590565b6000816014526f70a0823100000000000000000000000060005260208060246010865afa601f3d111660205102905092915050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61191b8382167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181169015011590565b60606000611b266020840184612f04565b9050600080611b386040860186612f34565b905011611b6b57611b66611b618373ffffffffffffffffffffffffffffffffffffffff16611c5b565b611c9d565b611bad565b611b786040850185612f34565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050505b9050611bb98183611d5d565b949350505050565b6060611202826000015183602001518460400151611d8f565b604080516060808201835260008083526020830152918101919091528160018351611c059190612f99565b81518110611c1557611c15612241565b60200260200101519050919050565b60005b8151811015610b2157611c52828281518110611c4557611c45612241565b6020026020010151611bc1565b50600101611c27565b6060611c6682611db2565b805161307882526002017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90910190815292915050565b606060006040518060400160405280601481526020017f656d70747944617461466f724f70657261746f7200000000000000000000000081525083604051602001611ce9929190612fac565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201207fffffffff00000000000000000000000000000000000000000000000000000000169083015280516004818403018152602490920190529392505050565b604051606090611d7890849060009085851b90602001613003565b604051602081830303815290604052905092915050565b6060611bb973ffffffffffffffffffffffffffffffffffffffff85168385611e3f565b60606040519050608081016040526f30313233343536373839616263646566600f526002810190506028815260208101600060288201528260601b925060005b808101820184821a600f81165160018301538060041c518253505060018101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed01611df2575050919050565b606081471015611e7d576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401610487565b6000808573ffffffffffffffffffffffffffffffffffffffff168486604051611ea69190613074565b60006040518083038185875af1925050503d8060008114611ee3576040519150601f19603f3d011682016040523d82523d6000602084013e611ee8565b606091505b5091509150611ef8868383611f02565b9695505050505050565b606082611f1757611f1282611f91565b61191b565b8151158015611f3b575073ffffffffffffffffffffffffffffffffffffffff84163b155b15611f8a576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401610487565b508061191b565b805115611fa15780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060208284031215611fe557600080fd5b5035919050565b60008060208385031215611fff57600080fd5b823567ffffffffffffffff8082111561201757600080fd5b818501915085601f83011261202b57600080fd5b81358181111561203a57600080fd5b8660208260051b850101111561204f57600080fd5b60209290920196919550909350505050565b6000806020838503121561207457600080fd5b823567ffffffffffffffff8082111561208c57600080fd5b818501915085601f8301126120a057600080fd5b8135818111156120af57600080fd5b86602082850101111561204f57600080fd5b6000602082840312156120d357600080fd5b813567ffffffffffffffff8111156120ea57600080fd5b82016060818503121561191b57600080fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461122157600080fd5b600080600080848603608081121561213557600080fd5b8535612140816120fc565b9450602086013567ffffffffffffffff8082111561215d57600080fd5b818801915088601f83011261217157600080fd5b81358181111561218057600080fd5b8960208260061b850101111561219557600080fd5b60208301965080955050505060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820112156121d157600080fd5b509295919450926040019150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115611202576112026121df565b8181036000831280158383131683831282161715610974576109746121df565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618336030181126122a457600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126122e357600080fd5b830160208101925035905067ffffffffffffffff81111561230357600080fd5b8060051b360382131561231557600080fd5b9250929050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261235157600080fd5b830160208101925035905067ffffffffffffffff81111561237157600080fd5b80360382131561231557600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18336030181126123fd57600080fd5b90910192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126123fd57600080fd5b80358252600061244d602083018361231c565b60406020860152612462604086018284612380565b95945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126124a057600080fd5b830160208101925035905067ffffffffffffffff8111156124c057600080fd5b8060061b360382131561231557600080fd5b80356124dd816120fc565b73ffffffffffffffffffffffffffffffffffffffff168252602090810135910152565b81835260208301925060008160005b848110156125345761252186836124d2565b604095860195919091019060010161250f565b5093949350505050565b60008383855260208086019550808560051b8301018460005b87811015612621577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08584030189526125908288612406565b604061259c828361231c565b8287526125ac8388018284612380565b9150506125bb878401846123c9565b92508581038787015260606125d08485612406565b8183526125df8284018261243a565b9150506125ee8885018561246b565b8383038a850152612600838284612500565b95850135939094019290925250505098840198925090830190600101612557565b5090979650505050505050565b60008383855260208086019550808560051b8301018460005b87811015612621577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08584030189526126808288612406565b604061268c828361231c565b82875261269c8388018284612380565b9150506126ab878401846123c9565b92508581038787015260606126c08485612406565b8183526126cf8284018261243a565b91505087840135888301526126e68385018561246b565b9450828203848401526126fa828683612500565b9d89019d9750505093860193505050600101612647565b60008383855260208086019550808560051b830101846000805b88811015612856577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0868503018a526127648389612406565b6040612770828361231c565b8288526127808389018284612380565b915050878301357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618436030181126127b6578586fd5b878203898901529092019160a06127cd848061231c565b8284526127dd8385018284612380565b925050506127ed8985018561231c565b8383038b8501526127ff838284612380565b8686013595850195909552505050606080840135908201526080928301359291612828846120fc565b73ffffffffffffffffffffffffffffffffffffffff939093169101529985019993509184019160010161272b565b509198975050505050505050565b60208152600061287483846122ae565b60a060208501528060c08501527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8111156128ae57600080fd5b60051b808260e0860137830190506128c960208501856122ae565b60c08584030160408601526128e260e08401828461253e565b925050506128f360408501856122ae565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086850301606087015261292984838561262e565b935061293860608801886122ae565b9350915080868503016080870152612951848484612711565b935061296060808801886122ae565b93509150808685030160a087015250611ef8838383612711565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff811182821017156129cc576129cc61297a565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612a1957612a1961297a565b604052919050565b600067ffffffffffffffff821115612a3b57612a3b61297a565b5060051b60200190565b60005b83811015612a60578181015183820152602001612a48565b50506000910152565b60006020808385031215612a7c57600080fd5b825167ffffffffffffffff80821115612a9457600080fd5b818501915085601f830112612aa857600080fd5b8151612abb612ab682612a21565b6129d2565b81815260059190911b83018401908481019088831115612ada57600080fd5b8585015b83811015612bd957805185811115612af65760008081fd5b860160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828d038101821315612b2d5760008081fd5b612b356129a9565b8a840151612b42816120fc565b81526040848101518c830152928401519289841115612b615760008081fd5b83850194508e603f860112612b7857600093508384fd5b8b850151935089841115612b8e57612b8e61297a565b612b9e8c84601f870116016129d2565b92508383528e81858701011115612bb55760008081fd5b612bc4848d8501838801612a45565b81019190915285525050918601918601612ade565b5098975050505050505050565b60008151808452612bfe816020860160208601612a45565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600060208083018184528085518083526040925060408601915060408160051b87010184880160005b83811015612cd7578883037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00185528151805173ffffffffffffffffffffffffffffffffffffffff16845287810151888501528601516060878501819052612cc381860183612be6565b968901969450505090860190600101612c59565b509098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff85168152608060208201819052810183905260008460a08301825b86811015612d3a57612d2782846124d2565b6040928301929190910190600101612d15565b509150612462905060408301846124d2565b600060408284031215612d5e57600080fd5b6040516040810181811067ffffffffffffffff82111715612d8157612d8161297a565b6040529050808235612d92816120fc565b8152602092830135920191909152919050565b600080600060808486031215612dba57600080fd5b8335612dc5816120fc565b925060208481013567ffffffffffffffff811115612de257600080fd5b8501601f81018713612df357600080fd5b8035612e01612ab682612a21565b8082825260208201915060208360061b850101925089831115612e2357600080fd5b6020840193505b82841015612e4e57612e3c8a85612d4c565b82528482019150604084019350612e2a565b8096505050505050612e638560408601612d4c565b90509250925092565b606080825283519082018190526000906020906080840190828701845b82811015612ecf57612ebc848351805173ffffffffffffffffffffffffffffffffffffffff168252602090810151910152565b6040939093019290840190600101612e89565b505050809250505061191b6020830184805173ffffffffffffffffffffffffffffffffffffffff168252602090810151910152565b600060208284031215612f1657600080fd5b813561191b816120fc565b60208152600061191b6020830184612be6565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612f6957600080fd5b83018035915067ffffffffffffffff821115612f8457600080fd5b60200191503681900382131561231557600080fd5b81810381811115611202576112026121df565b60008351612fbe818460208801612a45565b835190830190612fd2818360208801612a45565b7f28290000000000000000000000000000000000000000000000000000000000009101908152600201949350505050565b60008451613015818460208901612a45565b7fffffffffffffffffffffffff000000000000000000000000000000000000000094909416919093019081527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000091909116600c82015260200192915050565b600082516122a4818460208701612a4556fea2646970667358221220478a563657a501e95241c87ca953993d15f1fc3584c91a145ded550d04c66d6164736f6c6343000816003300000000000000000000000060e81bcfd3f0d7fe151f3ac37252aed81a74d11600000000000000000000000006c950420ef3b0676915d85db1b051892c49b88f

Deployed Bytecode

0x6080604052600436106101115760003560e01c80636290865d116100a55780638da5cb5b11610074578063c1611f6211610059578063c1611f62146103a2578063ec8accbc146103c2578063f966aac5146103e257600080fd5b80638da5cb5b1461037a57806390631d8e1461038f57600080fd5b80636290865d146102a757806377e747ca146102db5780638129fc1c14610331578063866517e81461034657600080fd5b80633db0a3e4116100e15780633db0a3e414610225578063506450e91461024557806356a5c6e8146102675780635c1c6dcd1461028757600080fd5b80624006e01461011d5780632c86d98e1461017b5780632cd16a8a146101d8578063354030231461020257600080fd5b3661011857005b600080fd5b34801561012957600080fd5b506101517f00000000000000000000000060e81bcfd3f0d7fe151f3ac37252aed81a74d11681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561018757600080fd5b506000546001546101ac9173ffffffffffffffffffffffffffffffffffffffff169082565b6040805173ffffffffffffffffffffffffffffffffffffffff9093168352602083019190915201610172565b3480156101e457600080fd5b506101ed610402565b60408051928352602083019190915201610172565b610215610210366004611fd3565b610418565b6040519015158152602001610172565b34801561023157600080fd5b50610215610240366004611fec565b61065c565b34801561025157600080fd5b50610265610260366004612061565b61097b565b005b34801561027357600080fd5b50610265610282366004612061565b610a10565b34801561029357600080fd5b506102656102a23660046120c1565b610ad7565b3480156102b357600080fd5b506101517f00000000000000000000000006c950420ef3b0676915d85db1b051892c49b88f81565b3480156102e757600080fd5b506102f0610b25565b60408051958652602086019490945273ffffffffffffffffffffffffffffffffffffffff92831693850193909352166060830152608082015260a001610172565b34801561033d57600080fd5b50610265610b6d565b34801561035257600080fd5b506101517f00000000000000000000000006c950420ef3b0676915d85db1b051892c49b88f81565b34801561038657600080fd5b50610151610e77565b61026561039d36600461211e565b610e86565b3480156103ae57600080fd5b506102656103bd366004612061565b610fdc565b3480156103ce57600080fd5b506102656103dd366004612061565b611071565b3480156103ee57600080fd5b506102656103fd366004612061565b611163565b60008061040d6111ad565b600354915091509091565b60003373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000060e81bcfd3f0d7fe151f3ac37252aed81a74d1161614610490576040517f32b2baa30000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000026104c36104bc6111ad565b82906111fc565b61050b57806104d06111ad565b6040517f8e653d8c00000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401610487565b82600003610561576000546040517f3b9b86ec00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610487565b8260026000828254610573919061220e565b909155506105819050610e77565b73ffffffffffffffffffffffffffffffffffffffff167fd81dc1ab773745d6113d7b56db63d143b38c598946eaf907995eeba71ee28799846002546000600101546105cc9190612221565b6040805192835260208301919091520160405180910390a260015460025403610621576106187f0000000000000000000000000000000000000000000000000000000000000004611208565b60019150610656565b6001546002541115610656576106567f0000000000000000000000000000000000000000000000000000000000000008611208565b50919050565b60003373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000060e81bcfd3f0d7fe151f3ac37252aed81a74d11616146106cf576040517f32b2baa3000000000000000000000000000000000000000000000000000000008152336004820152602401610487565b7f00000000000000000000000000000000000000000000000000000000000000087f00000000000000000000000000000000000000000000000000000000000000041761071d6104bc6111ad565b61072a57806104d06111ad565b6000839003610765576040517fe1d362ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107956107706111ad565b7f00000000000000000000000000000000000000000000000000000000000000041490565b156107c3576107c37f0000000000000000000000000000000000000000000000000000000000000008611208565b60005b838110156109455760007f00000000000000000000000006c950420ef3b0676915d85db1b051892c49b88f73ffffffffffffffffffffffffffffffffffffffff16634763f39d87878581811061081e5761081e612241565b90506020028101906108309190612270565b6040518263ffffffff1660e01b815260040161084c9190612864565b6000604051808303816000875af115801561086b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526108b19190810190612a69565b6040517fd670be0c00000000000000000000000000000000000000000000000000000000815290915073776197c47823dd8a2dd318d81a0556a9e4fb3b959063d670be0c90610904908490600401612c30565b60006040518083038186803b15801561091c57600080fd5b505af492505050801561092d575060015b61093c57600093505050610974565b506001016107c6565b5061096f7f0000000000000000000000000000000000000000000000000000000000000001611208565b600191505b5092915050565b3330146109b4576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109bd42600355565b60006002556109ca610e77565b73ffffffffffffffffffffffffffffffffffffffff167f89e8e0cb75caa917b6632d3053b50971462790ea99d9fe21b4b27ce71017515660405160405180910390a25050565b333014610a49576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5242600355565b6000610a5c610e77565b9050610a66611224565b50600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155600181905560405173ffffffffffffffffffffffffffffffffffffffff8316917fa29911196d428d7968f8bde7515181a391bfa16e26042f789f3f2da7665e25de91a2505050565b610adf6112ec565b7f0000000000000000000000000000000000000000000000000000000000000004610b0b6104bc6111ad565b610b1857806104d06111ad565b610b218261135c565b5050565b6000806000806000610b356111ad565b600354610b40610e77565b600054600154939992985090965073ffffffffffffffffffffffffffffffffffffffff1694509092509050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0080546002919068010000000000000000900460ff1680610bbc5750805467ffffffffffffffff808416911610155b15610bf3576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001667ffffffffffffffff83161768010000000000000000178155610c807f80000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000023063ec8accbc61136e565b610cd07f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000000000000000023063ec8accbc61136e565b610d207f00000000000000000000000000000000000000000000000000000000000000027f00000000000000000000000000000000000000000000000000000000000000043063c1611f6261136e565b610d707f00000000000000000000000000000000000000000000000000000000000000027f00000000000000000000000000000000000000000000000000000000000000083063506450e961136e565b610dc07f00000000000000000000000000000000000000000000000000000000000000047f00000000000000000000000000000000000000000000000000000000000000083063f966aac561136e565b610e107f00000000000000000000000000000000000000000000000000000000000000087f0000000000000000000000000000000000000000000000000000000000000001306356a5c6e861136e565b80547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16815560405167ffffffffffffffff831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050565b6000610e81611472565b905090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000060e81bcfd3f0d7fe151f3ac37252aed81a74d1161614610ef7576040517f32b2baa3000000000000000000000000000000000000000000000000000000008152336004820152602401610487565b7f00000000000000000000000000000000000000000000000000000000000000017f800000000000000000000000000000000000000000000000000000000000000017610f456104bc6111ad565b610f5257806104d06111ad565b60005b83811015610f8557610f7d858583818110610f7257610f72612241565b90506040020161149c565b600101610f55565b50610fd57f000000000000000000000000000000000000000000000000000000000000000286868686604051602001610fc19493929190612ce5565b6040516020818303038152906040526115dc565b5050505050565b333014611015576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61101e42600355565b600060025561102b610e77565b73ffffffffffffffffffffffffffffffffffffffff167f2077f6e031a8faa17b96131641b087822c10522fe1a94841987eccbe8f619f7f60405160405180910390a25050565b3330146110aa576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110b342600355565b600080806110c384860186612da5565b9250925092506110d2836117d3565b508051600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9283161790556020820151600155604051908416907f5f0651ae754e97c1f9b53e213b4ac64372e4f4441d6e6851b17bd129b4426076906111549085908590612e6c565b60405180910390a25050505050565b33301461119c576040517f980d854800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111a542600355565b6109ca610e77565b7fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30d54806111f957507f800000000000000000000000000000000000000000000000000000000000000090565b90565b81811615155b92915050565b61122181604051806020016040528060008152506115dc565b50565b60008061122f611472565b91508173ffffffffffffffffffffffffffffffffffffffff160361127f576040517fbd704aae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112a860007f608a8c8eefc529e0455c0b7a17a3c2a1a40a7f0cd5045e3b46a4aa8ae853476d55565b60405173ffffffffffffffffffffffffffffffffffffffff8216907f870217a7bae08cb1e27777025a86e87ce4df51b0e2c6cc144ed2976e78a5c79a90600090a290565b6112f4611472565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461135a576040517f32b2baa3000000000000000000000000000000000000000000000000000000008152336004820152602401610487565b565b61122181611369836118b3565b611922565b600061137a858561195a565b60008181527fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30e6020526040902054909150156113ec576040517fb57df58c0000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610487565b60009081527fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30e6020908152604090912080547fffffffffffffffff0000000000000000000000000000000000000000000000001663ffffffff939093169390911b77ffffffffffffffffffffffffffffffffffffffff0000000016929092171790555050565b6000610e817f608a8c8eefc529e0455c0b7a17a3c2a1a40a7f0cd5045e3b46a4aa8ae853476d5490565b8060200135600003611500576114b56020820182612f04565b6040517f3b9b86ec00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610487565b600061152d6115126020840184612f04565b73ffffffffffffffffffffffffffffffffffffffff16611a3b565b6115635761155e306115426020850185612f04565b73ffffffffffffffffffffffffffffffffffffffff1690611a8b565b611565565b475b90508160200135811015610b21576115806020830183612f04565b6040517f43bb149000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526020830135602482015260448101829052606401610487565b6115e582611ac0565b61161e576040517f815506a800000000000000000000000000000000000000000000000000000000815260048101839052602401610487565b600061163161162b6111ad565b8461195a565b60008181527fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30e60205260409020549091506116ab5761166e6111ad565b6040517f8d157bec000000000000000000000000000000000000000000000000000000008152600481019190915260248101849052604401610487565b7f1c0c6794b8c626515ef0eaa59cf29f712f686771b359ff1a5a05738b92d054846116d46111ad565b60408051918252602082018690520160405180910390a17fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30d83905560008181527fde4001bbdfdfed078acd4ae5c1023679bf2e3e2982cfd37f7c839d62304fe30e602090815260409182902054915160e083901b7fffffffff000000000000000000000000000000000000000000000000000000001681529082901c73ffffffffffffffffffffffffffffffffffffffff169163ffffffff169061179c908590600401612f21565b600060405180830381600087803b1580156117b657600080fd5b505af11580156117ca573d6000803e3d6000fd5b50505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8216611822576040517f55e7da0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61182a611472565b9050611854827f608a8c8eefc529e0455c0b7a17a3c2a1a40a7f0cd5045e3b46a4aa8ae853476d55565b8173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f76122449b9f9900601b55986d5b53d8d3f84b676f9d216a64334a914d21accd160405160405180910390a3919050565b6060600060405180606001604052807f00000000000000000000000006c950420ef3b0676915d85db1b051892c49b88f73ffffffffffffffffffffffffffffffffffffffff1681526020016000815260200161190e85611b15565b9052905061191b81611bc1565b9392505050565b6000818060200190518101906119389190612a69565b9050826020013561194882611bda565b6020015261195581611c24565b505050565b60007f80000000000000000000000000000000000000000000000000000000000000008314158015611992575061199083611ac0565b155b156119cc576040517f815506a800000000000000000000000000000000000000000000000000000000815260048101849052602401610487565b6119d582611ac0565b611a0e576040517f815506a800000000000000000000000000000000000000000000000000000000815260048101839052602401610487565b50604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b600073ffffffffffffffffffffffffffffffffffffffff821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148061120257505073ffffffffffffffffffffffffffffffffffffffff161590565b6000816014526f70a0823100000000000000000000000060005260208060246010865afa601f3d111660205102905092915050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61191b8382167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181169015011590565b60606000611b266020840184612f04565b9050600080611b386040860186612f34565b905011611b6b57611b66611b618373ffffffffffffffffffffffffffffffffffffffff16611c5b565b611c9d565b611bad565b611b786040850185612f34565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050505b9050611bb98183611d5d565b949350505050565b6060611202826000015183602001518460400151611d8f565b604080516060808201835260008083526020830152918101919091528160018351611c059190612f99565b81518110611c1557611c15612241565b60200260200101519050919050565b60005b8151811015610b2157611c52828281518110611c4557611c45612241565b6020026020010151611bc1565b50600101611c27565b6060611c6682611db2565b805161307882526002017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90910190815292915050565b606060006040518060400160405280601481526020017f656d70747944617461466f724f70657261746f7200000000000000000000000081525083604051602001611ce9929190612fac565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201207fffffffff00000000000000000000000000000000000000000000000000000000169083015280516004818403018152602490920190529392505050565b604051606090611d7890849060009085851b90602001613003565b604051602081830303815290604052905092915050565b6060611bb973ffffffffffffffffffffffffffffffffffffffff85168385611e3f565b60606040519050608081016040526f30313233343536373839616263646566600f526002810190506028815260208101600060288201528260601b925060005b808101820184821a600f81165160018301538060041c518253505060018101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed01611df2575050919050565b606081471015611e7d576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401610487565b6000808573ffffffffffffffffffffffffffffffffffffffff168486604051611ea69190613074565b60006040518083038185875af1925050503d8060008114611ee3576040519150601f19603f3d011682016040523d82523d6000602084013e611ee8565b606091505b5091509150611ef8868383611f02565b9695505050505050565b606082611f1757611f1282611f91565b61191b565b8151158015611f3b575073ffffffffffffffffffffffffffffffffffffffff84163b155b15611f8a576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401610487565b508061191b565b805115611fa15780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060208284031215611fe557600080fd5b5035919050565b60008060208385031215611fff57600080fd5b823567ffffffffffffffff8082111561201757600080fd5b818501915085601f83011261202b57600080fd5b81358181111561203a57600080fd5b8660208260051b850101111561204f57600080fd5b60209290920196919550909350505050565b6000806020838503121561207457600080fd5b823567ffffffffffffffff8082111561208c57600080fd5b818501915085601f8301126120a057600080fd5b8135818111156120af57600080fd5b86602082850101111561204f57600080fd5b6000602082840312156120d357600080fd5b813567ffffffffffffffff8111156120ea57600080fd5b82016060818503121561191b57600080fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461122157600080fd5b600080600080848603608081121561213557600080fd5b8535612140816120fc565b9450602086013567ffffffffffffffff8082111561215d57600080fd5b818801915088601f83011261217157600080fd5b81358181111561218057600080fd5b8960208260061b850101111561219557600080fd5b60208301965080955050505060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820112156121d157600080fd5b509295919450926040019150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115611202576112026121df565b8181036000831280158383131683831282161715610974576109746121df565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618336030181126122a457600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126122e357600080fd5b830160208101925035905067ffffffffffffffff81111561230357600080fd5b8060051b360382131561231557600080fd5b9250929050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261235157600080fd5b830160208101925035905067ffffffffffffffff81111561237157600080fd5b80360382131561231557600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18336030181126123fd57600080fd5b90910192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126123fd57600080fd5b80358252600061244d602083018361231c565b60406020860152612462604086018284612380565b95945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126124a057600080fd5b830160208101925035905067ffffffffffffffff8111156124c057600080fd5b8060061b360382131561231557600080fd5b80356124dd816120fc565b73ffffffffffffffffffffffffffffffffffffffff168252602090810135910152565b81835260208301925060008160005b848110156125345761252186836124d2565b604095860195919091019060010161250f565b5093949350505050565b60008383855260208086019550808560051b8301018460005b87811015612621577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08584030189526125908288612406565b604061259c828361231c565b8287526125ac8388018284612380565b9150506125bb878401846123c9565b92508581038787015260606125d08485612406565b8183526125df8284018261243a565b9150506125ee8885018561246b565b8383038a850152612600838284612500565b95850135939094019290925250505098840198925090830190600101612557565b5090979650505050505050565b60008383855260208086019550808560051b8301018460005b87811015612621577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08584030189526126808288612406565b604061268c828361231c565b82875261269c8388018284612380565b9150506126ab878401846123c9565b92508581038787015260606126c08485612406565b8183526126cf8284018261243a565b91505087840135888301526126e68385018561246b565b9450828203848401526126fa828683612500565b9d89019d9750505093860193505050600101612647565b60008383855260208086019550808560051b830101846000805b88811015612856577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0868503018a526127648389612406565b6040612770828361231c565b8288526127808389018284612380565b915050878301357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618436030181126127b6578586fd5b878203898901529092019160a06127cd848061231c565b8284526127dd8385018284612380565b925050506127ed8985018561231c565b8383038b8501526127ff838284612380565b8686013595850195909552505050606080840135908201526080928301359291612828846120fc565b73ffffffffffffffffffffffffffffffffffffffff939093169101529985019993509184019160010161272b565b509198975050505050505050565b60208152600061287483846122ae565b60a060208501528060c08501527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8111156128ae57600080fd5b60051b808260e0860137830190506128c960208501856122ae565b60c08584030160408601526128e260e08401828461253e565b925050506128f360408501856122ae565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086850301606087015261292984838561262e565b935061293860608801886122ae565b9350915080868503016080870152612951848484612711565b935061296060808801886122ae565b93509150808685030160a087015250611ef8838383612711565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff811182821017156129cc576129cc61297a565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612a1957612a1961297a565b604052919050565b600067ffffffffffffffff821115612a3b57612a3b61297a565b5060051b60200190565b60005b83811015612a60578181015183820152602001612a48565b50506000910152565b60006020808385031215612a7c57600080fd5b825167ffffffffffffffff80821115612a9457600080fd5b818501915085601f830112612aa857600080fd5b8151612abb612ab682612a21565b6129d2565b81815260059190911b83018401908481019088831115612ada57600080fd5b8585015b83811015612bd957805185811115612af65760008081fd5b860160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828d038101821315612b2d5760008081fd5b612b356129a9565b8a840151612b42816120fc565b81526040848101518c830152928401519289841115612b615760008081fd5b83850194508e603f860112612b7857600093508384fd5b8b850151935089841115612b8e57612b8e61297a565b612b9e8c84601f870116016129d2565b92508383528e81858701011115612bb55760008081fd5b612bc4848d8501838801612a45565b81019190915285525050918601918601612ade565b5098975050505050505050565b60008151808452612bfe816020860160208601612a45565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600060208083018184528085518083526040925060408601915060408160051b87010184880160005b83811015612cd7578883037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00185528151805173ffffffffffffffffffffffffffffffffffffffff16845287810151888501528601516060878501819052612cc381860183612be6565b968901969450505090860190600101612c59565b509098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff85168152608060208201819052810183905260008460a08301825b86811015612d3a57612d2782846124d2565b6040928301929190910190600101612d15565b509150612462905060408301846124d2565b600060408284031215612d5e57600080fd5b6040516040810181811067ffffffffffffffff82111715612d8157612d8161297a565b6040529050808235612d92816120fc565b8152602092830135920191909152919050565b600080600060808486031215612dba57600080fd5b8335612dc5816120fc565b925060208481013567ffffffffffffffff811115612de257600080fd5b8501601f81018713612df357600080fd5b8035612e01612ab682612a21565b8082825260208201915060208360061b850101925089831115612e2357600080fd5b6020840193505b82841015612e4e57612e3c8a85612d4c565b82528482019150604084019350612e2a565b8096505050505050612e638560408601612d4c565b90509250925092565b606080825283519082018190526000906020906080840190828701845b82811015612ecf57612ebc848351805173ffffffffffffffffffffffffffffffffffffffff168252602090810151910152565b6040939093019290840190600101612e89565b505050809250505061191b6020830184805173ffffffffffffffffffffffffffffffffffffffff168252602090810151910152565b600060208284031215612f1657600080fd5b813561191b816120fc565b60208152600061191b6020830184612be6565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612f6957600080fd5b83018035915067ffffffffffffffff821115612f8457600080fd5b60200191503681900382131561231557600080fd5b81810381811115611202576112026121df565b60008351612fbe818460208801612a45565b835190830190612fd2818360208801612a45565b7f28290000000000000000000000000000000000000000000000000000000000009101908152600201949350505050565b60008451613015818460208901612a45565b7fffffffffffffffffffffffff000000000000000000000000000000000000000094909416919093019081527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000091909116600c82015260200192915050565b600082516122a4818460208701612a4556fea2646970667358221220478a563657a501e95241c87ca953993d15f1fc3584c91a145ded550d04c66d6164736f6c63430008160033

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

00000000000000000000000060e81bcfd3f0d7fe151f3ac37252aed81a74d11600000000000000000000000006c950420ef3b0676915d85db1b051892c49b88f

-----Decoded View---------------
Arg [0] : _marginEngine (address): 0x60E81bCfd3F0d7FE151F3aC37252Aed81A74D116
Arg [1] : _compliance (address): 0x06c950420EF3b0676915d85dB1b051892c49b88f

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000060e81bcfd3f0d7fe151f3ac37252aed81a74d116
Arg [1] : 00000000000000000000000006c950420ef3b0676915d85db1b051892c49b88f


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
Loading...
Loading
[ 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.