ETH Price: $3,863.02 (+0.09%)

Contract

0x83E13069aA457778ca349E0128927B417A2c2B3f

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

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.18+commit.87f61d96

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
paris EvmVersion
File 1 of 19 : Account.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import {Auth} from "src/utils/Auth.sol";
import {BytesLib} from "src/utils/uniswap/BytesLib.sol";
import {
    IAccount, IPerpsV2MarketConsolidated
} from "src/interfaces/IAccount.sol";
import {IFactory} from "src/interfaces/IFactory.sol";
import {IFuturesMarketManager} from
    "src/interfaces/synthetix/IFuturesMarketManager.sol";
import {IPermit2} from "src/interfaces/uniswap/IPermit2.sol";
import {ISettings} from "src/interfaces/ISettings.sol";
import {ISystemStatus} from "src/interfaces/synthetix/ISystemStatus.sol";
import {IOps} from "src/interfaces/gelato/IOps.sol";
import {IUniversalRouter} from "src/interfaces/uniswap/IUniversalRouter.sol";
import {IEvents} from "src/interfaces/IEvents.sol";
import {IPerpsV2ExchangeRate} from
    "src/interfaces/synthetix/IPerpsV2ExchangeRate.sol";
import {OpsReady} from "src/utils/gelato/OpsReady.sol";
import {SafeCast160} from "src/utils/uniswap/SafeCast160.sol";
import {IERC20} from "src/interfaces/token/IERC20.sol";
import {V3Path} from "src/utils/uniswap/V3Path.sol";

/// @title Kwenta Smart Margin Account Implementation
/// @author JaredBorders ([email protected]), JChiaramonte7 ([email protected])
/// @notice flexible smart margin account enabling users to trade on-chain derivatives
contract Account is IAccount, Auth, OpsReady {
    using V3Path for bytes;
    using BytesLib for bytes;
    using SafeCast160 for uint256;

    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IAccount
    bytes32 public constant VERSION = "2.1.0";

    /// @notice tracking code used when modifying positions
    bytes32 internal constant TRACKING_CODE = "KWENTA";

    /// @notice used to ensure the pyth provided price is sufficiently recent
    /// @dev price cannot be older than MAX_PRICE_LATENCY seconds
    uint256 internal constant MAX_PRICE_LATENCY = 120;

    /// @notice Uniswap's Universal Router command for swapping tokens
    /// @dev specifically for swapping exact tokens in for a non-exact amount of tokens out
    uint256 internal constant V3_SWAP_EXACT_IN = 0x00;

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    /// @notice address of the Smart Margin Account Factory
    IFactory internal immutable FACTORY;

    /// @notice address of the contract used by all accounts for emitting events
    /// @dev can be immutable due to the fact the events contract is
    /// upgraded alongside the account implementation
    IEvents internal immutable EVENTS;

    /// @notice address of the Synthetix ProxyERC20sUSD contract used as the margin asset
    /// @dev can be immutable due to the fact the sUSD contract is a proxy address
    IERC20 internal immutable MARGIN_ASSET;

    /// @notice address of the Synthetix PerpsV2ExchangeRate
    /// @dev used internally by Synthetix Perps V2 contracts to retrieve asset exchange rates
    IPerpsV2ExchangeRate internal immutable PERPS_V2_EXCHANGE_RATE;

    /// @notice address of the Synthetix FuturesMarketManager
    /// @dev the manager keeps track of which markets exist, and is the main window between
    /// perpsV2 markets and the rest of the synthetix system. It accumulates the total debt
    /// over all markets, and issues and burns sUSD on each market's behalf
    IFuturesMarketManager internal immutable FUTURES_MARKET_MANAGER;

    /// @notice address of the Synthetix SystemStatus
    /// @dev the system status contract is used to check if the system is operational
    ISystemStatus internal immutable SYSTEM_STATUS;

    /// @notice address of contract used to store global settings
    ISettings internal immutable SETTINGS;

    /// @notice address of Uniswap's Universal Router
    IUniversalRouter internal immutable UNISWAP_UNIVERSAL_ROUTER;

    /// @notice address of Uniswap's Permit2
    IPermit2 public immutable PERMIT2;

    /*//////////////////////////////////////////////////////////////
                                 STATE
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IAccount
    uint256 public committedMargin;

    /// @inheritdoc IAccount
    uint256 public conditionalOrderId;

    /// @notice track conditional orders by id
    mapping(uint256 id => ConditionalOrder order) internal conditionalOrders;

    /// @notice value used for reentrancy protection
    /// @dev nonReentrant checks that locked is NOT EQUAL to 2
    uint256 internal locked;

    /*//////////////////////////////////////////////////////////////
                               MODIFIERS
    //////////////////////////////////////////////////////////////*/

    modifier isAccountExecutionEnabled() {
        if (!SETTINGS.accountExecutionEnabled()) {
            revert AccountExecutionDisabled();
        }

        _;
    }

    modifier nonReentrant() {
        /// @dev locked is intially set to 0 due to the proxy nature of SM accounts
        /// however after the inital call to nonReentrant(), locked will be set to 1
        if (locked == 2) revert Reentrancy();
        locked = 2;

        _;

        locked = 1;
    }

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

    /// @dev set owner of implementation to zero address
    /// @param _params: constructor parameters (see IAccount.sol)
    constructor(AccountConstructorParams memory _params)
        Auth(address(0))
        OpsReady(_params.gelato, _params.ops)
    {
        FACTORY = IFactory(_params.factory);
        EVENTS = IEvents(_params.events);
        MARGIN_ASSET = IERC20(_params.marginAsset);
        PERPS_V2_EXCHANGE_RATE =
            IPerpsV2ExchangeRate(_params.perpsV2ExchangeRate);
        FUTURES_MARKET_MANAGER =
            IFuturesMarketManager(_params.futuresMarketManager);
        SYSTEM_STATUS = ISystemStatus(_params.systemStatus);
        SETTINGS = ISettings(_params.settings);
        UNISWAP_UNIVERSAL_ROUTER = IUniversalRouter(_params.universalRouter);
        PERMIT2 = IPermit2(_params.permit2);
    }

    /*//////////////////////////////////////////////////////////////
                                 VIEWS
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IAccount
    function getDelayedOrder(bytes32 _marketKey)
        external
        view
        override
        returns (IPerpsV2MarketConsolidated.DelayedOrder memory order)
    {
        // fetch delayed order data from Synthetix
        order = _getPerpsV2Market(_marketKey).delayedOrders(address(this));
    }

    /// @inheritdoc IAccount
    function checker(uint256 _conditionalOrderId)
        external
        view
        returns (bool canExec, bytes memory execPayload)
    {
        canExec = _validConditionalOrder(_conditionalOrderId);

        // calldata for execute func
        execPayload =
            abi.encodeCall(this.executeConditionalOrder, _conditionalOrderId);
    }

    /// @inheritdoc IAccount
    function freeMargin() public view override returns (uint256) {
        return MARGIN_ASSET.balanceOf(address(this)) - committedMargin;
    }

    /// @inheritdoc IAccount
    function getPosition(bytes32 _marketKey)
        public
        view
        override
        returns (IPerpsV2MarketConsolidated.Position memory position)
    {
        // fetch position data from Synthetix
        position = _getPerpsV2Market(_marketKey).positions(address(this));
    }

    /// @inheritdoc IAccount
    function getConditionalOrder(uint256 _conditionalOrderId)
        public
        view
        override
        returns (ConditionalOrder memory)
    {
        return conditionalOrders[_conditionalOrderId];
    }

    /*//////////////////////////////////////////////////////////////
                               OWNERSHIP
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IAccount
    function setInitialOwnership(address _owner) external override {
        if (msg.sender != address(FACTORY)) revert Unauthorized();
        owner = _owner;
        emit OwnershipTransferred(address(0), _owner);
    }

    /// @notice transfer ownership of account to new address
    /// @dev update factory's record of account ownership
    /// @param _newOwner: new account owner
    function transferOwnership(address _newOwner) public override {
        // will revert if msg.sender is *NOT* owner
        super.transferOwnership(_newOwner);

        // update the factory's record of owners and account addresses
        FACTORY.updateAccountOwnership({
            _newOwner: _newOwner,
            _oldOwner: msg.sender // verified to be old owner
        });
    }

    /*//////////////////////////////////////////////////////////////
                               EXECUTION
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IAccount
    function execute(Command[] calldata _commands, bytes[] calldata _inputs)
        external
        payable
        override
        nonReentrant
        isAccountExecutionEnabled
    {
        uint256 numCommands = _commands.length;
        if (_inputs.length != numCommands) {
            revert LengthMismatch();
        }

        // loop through all given commands and execute them
        for (uint256 commandIndex = 0; commandIndex < numCommands;) {
            _dispatch(_commands[commandIndex], _inputs[commandIndex]);
            unchecked {
                ++commandIndex;
            }
        }
    }

    /// @notice Decodes and executes the given command with the given inputs
    /// @param _command: The command type to execute
    /// @param _inputs: The inputs to execute the command with
    function _dispatch(Command _command, bytes calldata _inputs) internal {
        uint256 commandIndex = uint256(_command);

        if (commandIndex < 2 || commandIndex == 14 || commandIndex == 15) {
            /// @dev only owner can execute the following commands
            if (!isOwner()) revert Unauthorized();

            if (_command == Command.ACCOUNT_MODIFY_MARGIN) {
                // Command.ACCOUNT_MODIFY_MARGIN
                int256 amount;
                assembly {
                    amount := calldataload(_inputs.offset)
                }
                _modifyAccountMargin({_amount: amount});
            } else if (_command == Command.ACCOUNT_WITHDRAW_ETH) {
                uint256 amount;
                assembly {
                    amount := calldataload(_inputs.offset)
                }
                _withdrawEth({_amount: amount});
            } else if (_command == Command.UNISWAP_V3_SWAP) {
                // Command.UNISWAP_V3_SWAP
                uint256 amountIn;
                uint256 amountOutMin;
                bytes calldata path = _inputs.toBytes(2);
                assembly {
                    amountIn := calldataload(_inputs.offset)
                    amountOutMin := calldataload(add(_inputs.offset, 0x20))
                    // 0x40 offset is the path; decoded above
                }
                _uniswapV3Swap({
                    _amountIn: amountIn,
                    _amountOutMin: amountOutMin,
                    _path: path
                });
            } else {
                // Command.PERMIT2_PERMIT
                IPermit2.PermitSingle calldata permitSingle;
                assembly {
                    permitSingle := _inputs.offset
                }
                bytes calldata data = _inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5)
                PERMIT2.permit(msg.sender, permitSingle, data);
            }
        } else {
            /// @dev only owner and delegate(s) can execute the following commands
            if (!isAuth()) revert Unauthorized();

            if (commandIndex < 4) {
                if (_command == Command.PERPS_V2_MODIFY_MARGIN) {
                    // Command.PERPS_V2_MODIFY_MARGIN
                    address market;
                    int256 amount;
                    assembly {
                        market := calldataload(_inputs.offset)
                        amount := calldataload(add(_inputs.offset, 0x20))
                    }
                    _perpsV2ModifyMargin({_market: market, _amount: amount});
                } else {
                    // Command.PERPS_V2_WITHDRAW_ALL_MARGIN
                    address market;
                    assembly {
                        market := calldataload(_inputs.offset)
                    }
                    _perpsV2WithdrawAllMargin({_market: market});
                }
            } else if (commandIndex < 6) {
                if (_command == Command.PERPS_V2_SUBMIT_ATOMIC_ORDER) {
                    // Command.PERPS_V2_SUBMIT_ATOMIC_ORDER
                    address market;
                    int256 sizeDelta;
                    uint256 desiredFillPrice;
                    assembly {
                        market := calldataload(_inputs.offset)
                        sizeDelta := calldataload(add(_inputs.offset, 0x20))
                        desiredFillPrice :=
                            calldataload(add(_inputs.offset, 0x40))
                    }
                    _perpsV2SubmitAtomicOrder({
                        _market: market,
                        _sizeDelta: sizeDelta,
                        _desiredFillPrice: desiredFillPrice
                    });
                } else {
                    // Command.PERPS_V2_SUBMIT_DELAYED_ORDER
                    address market;
                    int256 sizeDelta;
                    uint256 desiredTimeDelta;
                    uint256 desiredFillPrice;
                    assembly {
                        market := calldataload(_inputs.offset)
                        sizeDelta := calldataload(add(_inputs.offset, 0x20))
                        desiredTimeDelta :=
                            calldataload(add(_inputs.offset, 0x40))
                        desiredFillPrice :=
                            calldataload(add(_inputs.offset, 0x60))
                    }
                    _perpsV2SubmitDelayedOrder({
                        _market: market,
                        _sizeDelta: sizeDelta,
                        _desiredTimeDelta: desiredTimeDelta,
                        _desiredFillPrice: desiredFillPrice
                    });
                }
            } else if (commandIndex < 8) {
                if (_command == Command.PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER)
                {
                    // Command.PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER
                    address market;
                    int256 sizeDelta;
                    uint256 desiredFillPrice;
                    assembly {
                        market := calldataload(_inputs.offset)
                        sizeDelta := calldataload(add(_inputs.offset, 0x20))
                        desiredFillPrice :=
                            calldataload(add(_inputs.offset, 0x40))
                    }
                    _perpsV2SubmitOffchainDelayedOrder({
                        _market: market,
                        _sizeDelta: sizeDelta,
                        _desiredFillPrice: desiredFillPrice
                    });
                } else {
                    // Command.PERPS_V2_CLOSE_POSITION
                    address market;
                    uint256 desiredFillPrice;
                    assembly {
                        market := calldataload(_inputs.offset)
                        desiredFillPrice :=
                            calldataload(add(_inputs.offset, 0x20))
                    }
                    _perpsV2ClosePosition({
                        _market: market,
                        _desiredFillPrice: desiredFillPrice
                    });
                }
            } else if (commandIndex < 10) {
                if (_command == Command.PERPS_V2_SUBMIT_CLOSE_DELAYED_ORDER) {
                    // Command.PERPS_V2_SUBMIT_CLOSE_DELAYED_ORDER
                    address market;
                    uint256 desiredTimeDelta;
                    uint256 desiredFillPrice;
                    assembly {
                        market := calldataload(_inputs.offset)
                        desiredTimeDelta :=
                            calldataload(add(_inputs.offset, 0x20))
                        desiredFillPrice :=
                            calldataload(add(_inputs.offset, 0x40))
                    }
                    _perpsV2SubmitCloseDelayedOrder({
                        _market: market,
                        _desiredTimeDelta: desiredTimeDelta,
                        _desiredFillPrice: desiredFillPrice
                    });
                } else {
                    // Command.PERPS_V2_SUBMIT_CLOSE_OFFCHAIN_DELAYED_ORDER
                    address market;
                    uint256 desiredFillPrice;
                    assembly {
                        market := calldataload(_inputs.offset)
                        desiredFillPrice :=
                            calldataload(add(_inputs.offset, 0x20))
                    }
                    _perpsV2SubmitCloseOffchainDelayedOrder({
                        _market: market,
                        _desiredFillPrice: desiredFillPrice
                    });
                }
            } else if (commandIndex < 12) {
                if (_command == Command.PERPS_V2_CANCEL_DELAYED_ORDER) {
                    // Command.PERPS_V2_CANCEL_DELAYED_ORDER
                    address market;
                    assembly {
                        market := calldataload(_inputs.offset)
                    }
                    _perpsV2CancelDelayedOrder({_market: market});
                } else {
                    // Command.PERPS_V2_CANCEL_OFFCHAIN_DELAYED_ORDER
                    address market;
                    assembly {
                        market := calldataload(_inputs.offset)
                    }
                    _perpsV2CancelOffchainDelayedOrder({_market: market});
                }
            } else if (commandIndex < 14) {
                if (_command == Command.GELATO_PLACE_CONDITIONAL_ORDER) {
                    // Command.GELATO_PLACE_CONDITIONAL_ORDER
                    bytes32 marketKey;
                    int256 marginDelta;
                    int256 sizeDelta;
                    uint256 targetPrice;
                    ConditionalOrderTypes conditionalOrderType;
                    uint256 desiredFillPrice;
                    bool reduceOnly;
                    assembly {
                        marketKey := calldataload(_inputs.offset)
                        marginDelta := calldataload(add(_inputs.offset, 0x20))
                        sizeDelta := calldataload(add(_inputs.offset, 0x40))
                        targetPrice := calldataload(add(_inputs.offset, 0x60))
                        conditionalOrderType :=
                            calldataload(add(_inputs.offset, 0x80))
                        desiredFillPrice :=
                            calldataload(add(_inputs.offset, 0xa0))
                        reduceOnly := calldataload(add(_inputs.offset, 0xc0))
                    }
                    _placeConditionalOrder({
                        _marketKey: marketKey,
                        _marginDelta: marginDelta,
                        _sizeDelta: sizeDelta,
                        _targetPrice: targetPrice,
                        _conditionalOrderType: conditionalOrderType,
                        _desiredFillPrice: desiredFillPrice,
                        _reduceOnly: reduceOnly
                    });
                } else {
                    // Command.GELATO_CANCEL_CONDITIONAL_ORDER
                    uint256 orderId;
                    assembly {
                        orderId := calldataload(_inputs.offset)
                    }
                    _cancelConditionalOrder({_conditionalOrderId: orderId});
                }
            } else if (commandIndex > 15) {
                // commandIndex 14 & 15 valid and already checked
                revert InvalidCommandType(commandIndex);
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        ACCOUNT DEPOSIT/WITHDRAW
    //////////////////////////////////////////////////////////////*/

    /// @notice allows ETH to be deposited directly into a margin account
    /// @notice ETH can be withdrawn
    receive() external payable {}

    /// @notice allow users to withdraw ETH deposited for keeper fees
    /// @param _amount: amount to withdraw
    function _withdrawEth(uint256 _amount) internal {
        if (_amount > 0) {
            (bool success,) = payable(msg.sender).call{value: _amount}("");
            if (!success) revert EthWithdrawalFailed();

            EVENTS.emitEthWithdraw({user: msg.sender, amount: _amount});
        }
    }

    /// @notice deposit/withdraw margin to/from this smart margin account
    /// @param _amount: amount of margin to deposit/withdraw
    function _modifyAccountMargin(int256 _amount) internal {
        // if amount is positive, deposit
        if (_amount > 0) {
            /// @dev failed Synthetix asset transfer will revert and not return false if unsuccessful
            MARGIN_ASSET.transferFrom(msg.sender, address(this), _abs(_amount));

            EVENTS.emitDeposit({user: msg.sender, amount: _abs(_amount)});
        } else if (_amount < 0) {
            // if amount is negative, withdraw
            _sufficientMargin(_amount);

            /// @dev failed Synthetix asset transfer will revert and not return false if unsuccessful
            MARGIN_ASSET.transfer(msg.sender, _abs(_amount));

            EVENTS.emitWithdraw({user: msg.sender, amount: _abs(_amount)});
        }
    }

    /*//////////////////////////////////////////////////////////////
                          MODIFY MARKET MARGIN
    //////////////////////////////////////////////////////////////*/

    /// @notice deposit/withdraw margin to/from a Synthetix PerpsV2 Market
    /// @param _market: address of market
    /// @param _amount: amount of margin to deposit/withdraw
    function _perpsV2ModifyMargin(address _market, int256 _amount) internal {
        if (_amount > 0) {
            _sufficientMargin(_amount);
        }
        IPerpsV2MarketConsolidated(_market).transferMargin(_amount);
    }

    /// @notice withdraw margin from market back to this account
    /// @dev this will *not* fail if market has zero margin
    function _perpsV2WithdrawAllMargin(address _market) internal {
        IPerpsV2MarketConsolidated(_market).withdrawAllMargin();
    }

    /*//////////////////////////////////////////////////////////////
                             ATOMIC ORDERS
    //////////////////////////////////////////////////////////////*/

    /// @notice submit an atomic order to a Synthetix PerpsV2 Market
    /// @dev atomic orders are executed immediately and incur a *significant* fee
    /// @param _market: address of market
    /// @param _sizeDelta: size delta of order
    /// @param _desiredFillPrice: desired fill price of order
    function _perpsV2SubmitAtomicOrder(
        address _market,
        int256 _sizeDelta,
        uint256 _desiredFillPrice
    ) internal {
        IPerpsV2MarketConsolidated(_market).modifyPositionWithTracking({
            sizeDelta: _sizeDelta,
            desiredFillPrice: _desiredFillPrice,
            trackingCode: TRACKING_CODE
        });
    }

    /// @notice close Synthetix PerpsV2 Market position via an atomic order
    /// @param _market: address of market
    /// @param _desiredFillPrice: desired fill price of order
    function _perpsV2ClosePosition(address _market, uint256 _desiredFillPrice)
        internal
    {
        // close position (i.e. reduce size to zero)
        /// @dev this does not remove margin from market
        IPerpsV2MarketConsolidated(_market).closePositionWithTracking({
            desiredFillPrice: _desiredFillPrice,
            trackingCode: TRACKING_CODE
        });
    }

    /*//////////////////////////////////////////////////////////////
                             DELAYED ORDERS
    //////////////////////////////////////////////////////////////*/

    /// @notice submit a delayed order to a Synthetix PerpsV2 Market
    /// @param _market: address of market
    /// @param _sizeDelta: size delta of order
    /// @param _desiredTimeDelta: desired time delta of order
    /// @param _desiredFillPrice: desired fill price of order
    function _perpsV2SubmitDelayedOrder(
        address _market,
        int256 _sizeDelta,
        uint256 _desiredTimeDelta,
        uint256 _desiredFillPrice
    ) internal {
        IPerpsV2MarketConsolidated(_market).submitDelayedOrderWithTracking({
            sizeDelta: _sizeDelta,
            desiredTimeDelta: _desiredTimeDelta,
            desiredFillPrice: _desiredFillPrice,
            trackingCode: TRACKING_CODE
        });
    }

    /// @notice cancel a *pending* delayed order from a Synthetix PerpsV2 Market
    /// @dev will revert if no previous delayed order
    function _perpsV2CancelDelayedOrder(address _market) internal {
        IPerpsV2MarketConsolidated(_market).cancelDelayedOrder(address(this));
    }

    /// @notice close Synthetix PerpsV2 Market position via a delayed order
    /// @param _market: address of market
    /// @param _desiredTimeDelta: desired time delta of order
    /// @param _desiredFillPrice: desired fill price of order
    function _perpsV2SubmitCloseDelayedOrder(
        address _market,
        uint256 _desiredTimeDelta,
        uint256 _desiredFillPrice
    ) internal {
        // close position (i.e. reduce size to zero)
        /// @dev this does not remove margin from market
        IPerpsV2MarketConsolidated(_market).submitCloseDelayedOrderWithTracking({
            desiredTimeDelta: _desiredTimeDelta,
            desiredFillPrice: _desiredFillPrice,
            trackingCode: TRACKING_CODE
        });
    }

    /*//////////////////////////////////////////////////////////////
                        DELAYED OFF-CHAIN ORDERS
    //////////////////////////////////////////////////////////////*/

    /// @notice submit an off-chain delayed order to a Synthetix PerpsV2 Market
    /// @param _market: address of market
    /// @param _sizeDelta: size delta of order
    /// @param _desiredFillPrice: desired fill price of order
    function _perpsV2SubmitOffchainDelayedOrder(
        address _market,
        int256 _sizeDelta,
        uint256 _desiredFillPrice
    ) internal {
        IPerpsV2MarketConsolidated(_market)
            .submitOffchainDelayedOrderWithTracking({
            sizeDelta: _sizeDelta,
            desiredFillPrice: _desiredFillPrice,
            trackingCode: TRACKING_CODE
        });
    }

    /// @notice cancel a *pending* off-chain delayed order from a Synthetix PerpsV2 Market
    /// @dev will revert if no previous offchain delayed order
    function _perpsV2CancelOffchainDelayedOrder(address _market) internal {
        IPerpsV2MarketConsolidated(_market).cancelOffchainDelayedOrder(
            address(this)
        );
    }

    /// @notice close Synthetix PerpsV2 Market position via an offchain delayed order
    /// @param _market: address of market
    /// @param _desiredFillPrice: desired fill price of order
    function _perpsV2SubmitCloseOffchainDelayedOrder(
        address _market,
        uint256 _desiredFillPrice
    ) internal {
        // close position (i.e. reduce size to zero)
        /// @dev this does not remove margin from market
        IPerpsV2MarketConsolidated(_market)
            .submitCloseOffchainDelayedOrderWithTracking({
            desiredFillPrice: _desiredFillPrice,
            trackingCode: TRACKING_CODE
        });
    }

    /*//////////////////////////////////////////////////////////////
                        CREATE CONDITIONAL ORDER
    //////////////////////////////////////////////////////////////*/

    /// @notice register a conditional order internally and with gelato
    /// @dev restricts _sizeDelta to be non-zero otherwise no need for conditional order
    /// @param _marketKey: Synthetix futures market id/key
    /// @param _marginDelta: amount of margin (in sUSD) to deposit or withdraw
    /// @param _sizeDelta: denominated in market currency (i.e. ETH, BTC, etc), size of position
    /// @param _targetPrice: expected conditional order price
    /// @param _conditionalOrderType: expected conditional order type enum where 0 = LIMIT, 1 = STOP, etc..
    /// @param _desiredFillPrice: desired price to fill Synthetix PerpsV2 order at execution time
    /// @param _reduceOnly: if true, only allows position's absolute size to decrease
    function _placeConditionalOrder(
        bytes32 _marketKey,
        int256 _marginDelta,
        int256 _sizeDelta,
        uint256 _targetPrice,
        ConditionalOrderTypes _conditionalOrderType,
        uint256 _desiredFillPrice,
        bool _reduceOnly
    ) internal {
        if (_sizeDelta == 0) revert ZeroSizeDelta();

        // if more margin is desired on the position we must commit the margin
        if (_marginDelta > 0) {
            _sufficientMargin(_marginDelta);
            committedMargin += _abs(_marginDelta);
        }

        // create and submit Gelato task for this conditional order
        bytes32 taskId = _createGelatoTask();

        // internally store the conditional order
        conditionalOrders[conditionalOrderId] = ConditionalOrder({
            marketKey: _marketKey,
            marginDelta: _marginDelta,
            sizeDelta: _sizeDelta,
            targetPrice: _targetPrice,
            gelatoTaskId: taskId,
            conditionalOrderType: _conditionalOrderType,
            desiredFillPrice: _desiredFillPrice,
            reduceOnly: _reduceOnly
        });

        EVENTS.emitConditionalOrderPlaced({
            conditionalOrderId: conditionalOrderId,
            gelatoTaskId: taskId,
            marketKey: _marketKey,
            marginDelta: _marginDelta,
            sizeDelta: _sizeDelta,
            targetPrice: _targetPrice,
            conditionalOrderType: _conditionalOrderType,
            desiredFillPrice: _desiredFillPrice,
            reduceOnly: _reduceOnly
        });

        ++conditionalOrderId;
    }

    /// @notice create a new Gelato task for a conditional order
    /// @return taskId of the new Gelato task
    function _createGelatoTask() internal returns (bytes32 taskId) {
        IOps.ModuleData memory moduleData = _createGelatoModuleData();

        taskId = IOps(OPS).createTask({
            execAddress: address(this),
            execData: abi.encodeCall(
                this.executeConditionalOrder, conditionalOrderId
                ),
            moduleData: moduleData,
            feeToken: ETH
        });
    }

    /// @notice create the Gelato ModuleData for a conditional order
    /// @dev see IOps for details on the task creation and the ModuleData struct
    function _createGelatoModuleData()
        internal
        view
        returns (IOps.ModuleData memory moduleData)
    {
        moduleData = IOps.ModuleData({
            modules: new IOps.Module[](1),
            args: new bytes[](1)
        });

        moduleData.modules[0] = IOps.Module.RESOLVER;
        moduleData.args[0] = abi.encode(
            address(this), abi.encodeCall(this.checker, conditionalOrderId)
        );
    }

    /*//////////////////////////////////////////////////////////////
                        CANCEL CONDITIONAL ORDER
    //////////////////////////////////////////////////////////////*/

    /// @notice cancel a gelato queued conditional order
    /// @param _conditionalOrderId: key for an active conditional order
    function _cancelConditionalOrder(uint256 _conditionalOrderId) internal {
        ConditionalOrder memory conditionalOrder =
            getConditionalOrder(_conditionalOrderId);

        // if margin was committed, free it
        if (conditionalOrder.marginDelta > 0) {
            committedMargin -= _abs(conditionalOrder.marginDelta);
        }

        // cancel gelato task
        /// @dev will revert if task id does not exist {Automate.cancelTask: Task not found}
        IOps(OPS).cancelTask({taskId: conditionalOrder.gelatoTaskId});

        // delete order from conditional orders
        delete conditionalOrders[_conditionalOrderId];

        EVENTS.emitConditionalOrderCancelled({
            conditionalOrderId: _conditionalOrderId,
            gelatoTaskId: conditionalOrder.gelatoTaskId,
            reason: ConditionalOrderCancelledReason
                .CONDITIONAL_ORDER_CANCELLED_BY_USER
        });
    }

    /*//////////////////////////////////////////////////////////////
                       EXECUTE CONDITIONAL ORDER
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IAccount
    function executeConditionalOrder(uint256 _conditionalOrderId)
        external
        override
        nonReentrant
        isAccountExecutionEnabled
    {
        // verify conditional order is ready for execution
        /// @dev it is understood this is a duplicate check if the executor is Gelato
        if (!_validConditionalOrder(_conditionalOrderId)) {
            revert CannotExecuteConditionalOrder({
                conditionalOrderId: _conditionalOrderId,
                executor: msg.sender
            });
        }

        // store conditional order object in memory
        ConditionalOrder memory conditionalOrder =
            getConditionalOrder(_conditionalOrderId);

        // remove conditional order from internal accounting
        delete conditionalOrders[_conditionalOrderId];

        // remove gelato task from their accounting
        /// @dev will revert if task id does not exist {Automate.cancelTask: Task not found}
        /// @dev if executor is not Gelato, the task will still be cancelled
        IOps(OPS).cancelTask({taskId: conditionalOrder.gelatoTaskId});

        // impose and record fee paid to executor
        uint256 fee = _payExecutorFee();

        // define Synthetix PerpsV2 market
        IPerpsV2MarketConsolidated market =
            _getPerpsV2Market(conditionalOrder.marketKey);

        /// @dev conditional order is valid given checker() returns true; define fill price
        (uint256 fillPrice, PriceOracleUsed priceOracle) = _sUSDRate(market);

        // if conditional order is reduce only, ensure position size is only reduced
        if (conditionalOrder.reduceOnly) {
            int256 currentSize = market.positions({account: address(this)}).size;

            // ensure position exists and incoming size delta is NOT the same sign
            /// @dev if incoming size delta is the same sign, then the conditional order is not reduce only
            if (
                currentSize == 0
                    || _isSameSign(currentSize, conditionalOrder.sizeDelta)
            ) {
                EVENTS.emitConditionalOrderCancelled({
                    conditionalOrderId: _conditionalOrderId,
                    gelatoTaskId: conditionalOrder.gelatoTaskId,
                    reason: ConditionalOrderCancelledReason
                        .CONDITIONAL_ORDER_CANCELLED_NOT_REDUCE_ONLY
                });

                return;
            }

            // ensure incoming size delta is not larger than current position size
            /// @dev reduce only conditional orders can only reduce position size (i.e. approach size of zero) and
            /// cannot cross that boundary (i.e. short -> long or long -> short)
            if (_abs(conditionalOrder.sizeDelta) > _abs(currentSize)) {
                // bound conditional order size delta to current position size
                conditionalOrder.sizeDelta = -currentSize;
            }
        }

        // if margin was committed, free it
        if (conditionalOrder.marginDelta > 0) {
            committedMargin -= _abs(conditionalOrder.marginDelta);
        }

        // execute trade
        _perpsV2ModifyMargin({
            _market: address(market),
            _amount: conditionalOrder.marginDelta
        });

        _perpsV2SubmitOffchainDelayedOrder({
            _market: address(market),
            _sizeDelta: conditionalOrder.sizeDelta,
            _desiredFillPrice: conditionalOrder.desiredFillPrice
        });

        EVENTS.emitConditionalOrderFilled({
            conditionalOrderId: _conditionalOrderId,
            gelatoTaskId: conditionalOrder.gelatoTaskId,
            fillPrice: fillPrice,
            keeperFee: fee,
            priceOracle: priceOracle
        });
    }

    /// @notice pay fee for conditional order execution
    /// @dev fee will be different depending on executor
    /// @return fee amount paid
    function _payExecutorFee() internal returns (uint256 fee) {
        if (msg.sender == OPS) {
            (fee,) = IOps(OPS).getFeeDetails();
            _transfer({_amount: fee});
        } else {
            fee = SETTINGS.executorFee();
            (bool success,) = msg.sender.call{value: fee}("");
            if (!success) revert CannotPayExecutorFee(fee, msg.sender);
        }
    }

    /// @notice order logic condition checker
    /// @dev this is where order type logic checks are handled
    /// @param _conditionalOrderId: key for an active order
    /// @return true if conditional order is valid by execution rules
    function _validConditionalOrder(uint256 _conditionalOrderId)
        internal
        view
        returns (bool)
    {
        ConditionalOrder memory conditionalOrder =
            getConditionalOrder(_conditionalOrderId);

        // return false if market key is the default value (i.e. "")
        if (conditionalOrder.marketKey == bytes32(0)) {
            return false;
        }

        // return false if market is paused
        try SYSTEM_STATUS.requireFuturesMarketActive(conditionalOrder.marketKey)
        {} catch {
            return false;
        }

        /// @dev if marketKey is invalid, this will revert
        (uint256 price,) =
            _sUSDRate(_getPerpsV2Market(conditionalOrder.marketKey));

        // check if markets satisfy specific order type
        if (
            conditionalOrder.conditionalOrderType == ConditionalOrderTypes.LIMIT
        ) {
            return _validLimitOrder(conditionalOrder, price);
        } else if (
            conditionalOrder.conditionalOrderType == ConditionalOrderTypes.STOP
        ) {
            return _validStopOrder(conditionalOrder, price);
        }

        // unknown order type
        return false;
    }

    /// @notice limit order logic condition checker
    /// @dev sizeDelta will never be zero due to check when submitting conditional order
    /// @param _conditionalOrder: struct for an active conditional order
    /// @param _price: current price of market asset
    /// @return true if conditional order is valid by execution rules
    function _validLimitOrder(
        ConditionalOrder memory _conditionalOrder,
        uint256 _price
    ) internal pure returns (bool) {
        if (_conditionalOrder.sizeDelta > 0) {
            // Long: increase position size (buy) once *below* target price
            // ex: open long position once price is below target
            return _price <= _conditionalOrder.targetPrice;
        } else {
            // Short: decrease position size (sell) once *above* target price
            // ex: open short position once price is above target
            return _price >= _conditionalOrder.targetPrice;
        }
    }

    /// @notice stop order logic condition checker
    /// @dev sizeDelta will never be zero due to check when submitting order
    /// @param _conditionalOrder: struct for an active conditional order
    /// @param _price: current price of market asset
    /// @return true if conditional order is valid by execution rules
    function _validStopOrder(
        ConditionalOrder memory _conditionalOrder,
        uint256 _price
    ) internal pure returns (bool) {
        if (_conditionalOrder.sizeDelta > 0) {
            // Long: increase position size (buy) once *above* target price
            // ex: unwind short position once price is above target (prevent further loss)
            return _price >= _conditionalOrder.targetPrice;
        } else {
            // Short: decrease position size (sell) once *below* target price
            // ex: unwind long position once price is below target (prevent further loss)
            return _price <= _conditionalOrder.targetPrice;
        }
    }

    /*//////////////////////////////////////////////////////////////
                                UNISWAP
    //////////////////////////////////////////////////////////////*/

    /// @notice swap tokens via Uniswap V3 (sUSD <-> whitelisted token)
    /// @dev assumes sufficient token allowances (i.e. Permit2 and this contract)
    /// @dev non-whitelisted connector tokens will NOT cause a revert
    /// (i.e. sUSD -> non-whitelisted token -> whitelisted token)
    /// @param _amountIn: amount of token to swap
    /// @param _amountOutMin: minimum amount of token to receive
    /// @param _path: path of tokens to swap (token0 - fee - token1)
    function _uniswapV3Swap(
        uint256 _amountIn,
        uint256 _amountOutMin,
        bytes calldata _path
    ) internal {
        // decode tokens to swap
        (address tokenIn, address tokenOut) = _getTokenInTokenOut(_path);

        // define recipient of swap; set later once direction is established
        address recipient;

        /// @dev verify direction and validity of swap (i.e. sUSD <-> whitelisted token)
        if (
            tokenIn == address(MARGIN_ASSET)
                && SETTINGS.isTokenWhitelisted(tokenOut)
        ) {
            // if swapping sUSD for another token, ensure sufficient margin
            /// @dev margin is being transferred out of this contract
            _sufficientMargin(int256(_amountIn));

            recipient = msg.sender;

            // transfer sUSD to the UniversalRouter for the swap
            /// @dev not using SafeERC20 because sUSD is a trusted token
            IERC20(tokenIn).transfer(
                address(UNISWAP_UNIVERSAL_ROUTER), _amountIn
            );
        } else if (
            tokenOut == address(MARGIN_ASSET)
                && SETTINGS.isTokenWhitelisted(tokenIn)
        ) {
            // if swapping another token for sUSD, incoming token must be transferred to the UniversalRouter
            /// @dev msg.sender must have approved Permit2 to spend at least the amountIn
            PERMIT2.transferFrom({
                from: msg.sender,
                to: address(UNISWAP_UNIVERSAL_ROUTER),
                amount: _amountIn.toUint160(),
                token: tokenIn
            });

            recipient = address(this);
        } else {
            // only allow sUSD <-> whitelisted token swaps
            revert TokenSwapNotAllowed(tokenIn, tokenOut);
        }

        _universalRouterExecute(recipient, _amountIn, _amountOutMin, _path);

        EVENTS.emitUniswapV3Swap({
            tokenIn: tokenIn,
            tokenOut: tokenOut,
            recipient: recipient,
            amountIn: _amountIn,
            amountOutMinimum: _amountOutMin
        });
    }

    /// @notice decode and return tokens encoded in the provided path
    /// @param _path: path of tokens to swap (token0 - fee - token1)
    /// @return tokenIn token swapped into the respective pool
    /// @return tokenOut token swapped out of the respective pool
    function _getTokenInTokenOut(bytes calldata _path)
        internal
        pure
        returns (address tokenIn, address tokenOut)
    {
        tokenIn = _path.decodeFirstToken();
        while (true) {
            bool hasMultiplePools = _path.hasMultiplePools();

            // decide whether to continue or terminate
            if (hasMultiplePools) {
                _path = _path.skipToken();
            } else {
                (,, tokenOut) = _path.toPool();
                break;
            }
        }
    }

    /// @notice call Uniswap's Universal Router to execute a swap
    /// @param _recipient: address to receive swapped tokens
    /// @param _amountIn: amount of token to swap
    /// @param _amountOutMin: minimum amount of token to receive
    /// @param _path: path of tokens to swap (token0 - fee - token1)
    function _universalRouterExecute(
        address _recipient,
        uint256 _amountIn,
        uint256 _amountOutMin,
        bytes calldata _path
    ) internal {
        /// @dev payerIsUser (i.e. 5th argument encoded) will always be false because
        /// tokens are transferred to the UniversalRouter before executing the swap
        bytes[] memory inputs = new bytes[](1);
        inputs[0] =
            abi.encode(_recipient, _amountIn, _amountOutMin, _path, false);

        UNISWAP_UNIVERSAL_ROUTER.execute({
            commands: abi.encodePacked(bytes1(uint8(V3_SWAP_EXACT_IN))),
            inputs: inputs
        });
    }

    /*//////////////////////////////////////////////////////////////
                            MARGIN UTILITIES
    //////////////////////////////////////////////////////////////*/

    /// @notice check that margin attempted to be moved/locked is within free margin bounds
    /// @param _marginOut: amount of margin to be moved/locked
    function _sufficientMargin(int256 _marginOut) internal view {
        if (_abs(_marginOut) > freeMargin()) {
            revert InsufficientFreeMargin(freeMargin(), _abs(_marginOut));
        }
    }

    /*//////////////////////////////////////////////////////////////
                            GETTER UTILITIES
    //////////////////////////////////////////////////////////////*/

    /// @notice fetch PerpsV2Market market defined by market key
    /// @param _marketKey: key for Synthetix PerpsV2 market
    /// @return market IPerpsV2MarketConsolidated contract interface
    function _getPerpsV2Market(bytes32 _marketKey)
        internal
        view
        returns (IPerpsV2MarketConsolidated market)
    {
        market = IPerpsV2MarketConsolidated(
            FUTURES_MARKET_MANAGER.marketForKey(_marketKey)
        );

        // sanity check
        assert(address(market) != address(0));
    }

    /// @notice get exchange rate of underlying market asset in terms of sUSD
    /// @dev _sUSDRate can only be reached after a successful internal call to _getPerpsV2Market
    /// which will revert if the market address returned is address(0)
    /// @param _market: Synthetix PerpsV2 Market
    /// @return price in sUSD
    function _sUSDRate(IPerpsV2MarketConsolidated _market)
        internal
        view
        returns (uint256, PriceOracleUsed)
    {
        /// @dev will revert if market is invalid
        bytes32 assetId = _market.baseAsset();

        /// @dev can revert if assetId is invalid OR there's no price for the given asset
        (uint256 price, uint256 publishTime) =
            PERPS_V2_EXCHANGE_RATE.resolveAndGetLatestPrice(assetId);

        // resolveAndGetLatestPrice is provide by pyth
        PriceOracleUsed priceOracle = PriceOracleUsed.PYTH;

        // if the price is stale, get the latest price from the market
        // (i.e. Chainlink provided price)
        if (publishTime < block.timestamp - MAX_PRICE_LATENCY) {
            // set price oracle used to Chainlink
            priceOracle = PriceOracleUsed.CHAINLINK;

            // fetch asset price and ensure it is valid
            bool invalid;
            (price, invalid) = _market.assetPrice();
            if (invalid) revert InvalidPrice();
        }

        /// @dev see IPerpsV2ExchangeRates to understand risks associated with this price
        return (price, priceOracle);
    }

    /*//////////////////////////////////////////////////////////////
                             MATH UTILITIES
    //////////////////////////////////////////////////////////////*/

    /// @notice get absolute value of the input, returned as an unsigned number.
    /// @param x: signed number
    /// @return z uint256 absolute value of x
    function _abs(int256 x) internal pure returns (uint256 z) {
        assembly {
            let mask := sub(0, shr(255, x))
            z := xor(mask, add(mask, x))
        }
    }

    /// @notice determines if input numbers have the same sign
    /// @dev asserts that both numbers are not zero
    /// @param x: signed number
    /// @param y: signed number
    /// @return true if same sign, false otherwise
    function _isSameSign(int256 x, int256 y) internal pure returns (bool) {
        assert(x != 0 && y != 0);
        return (x ^ y) >= 0;
    }
}

File 2 of 19 : Auth.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/// @notice Authorization mixin for Smart Margin Accounts
/// @author JaredBorders ([email protected])
/// @dev This contract is intended to be inherited by the Account contract
abstract contract Auth {
    /*//////////////////////////////////////////////////////////////
                                 STATE
    //////////////////////////////////////////////////////////////*/

    /// @notice owner of the account
    address public owner;

    /// @notice mapping of delegate address
    mapping(address delegate => bool) public delegates;

    /// @dev reserved storage space for future contract upgrades
    /// @custom:caution reduce storage size when adding new storage variables
    uint256[19] private __gap;

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice thrown when an unauthorized caller attempts
    /// to access a caller restricted function
    error Unauthorized();

    /// @notice thrown when the delegate address is invalid
    /// @param delegateAddress: address of the delegate attempting to be added
    error InvalidDelegateAddress(address delegateAddress);

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice emitted after ownership transfer
    /// @param caller: previous owner
    /// @param newOwner: new owner
    event OwnershipTransferred(
        address indexed caller, address indexed newOwner
    );

    /// @notice emitted after a delegate is added
    /// @param caller: owner of the account
    /// @param delegate: address of the delegate being added
    event DelegatedAccountAdded(
        address indexed caller, address indexed delegate
    );

    /// @notice emitted after a delegate is removed
    /// @param caller: owner of the account
    /// @param delegate: address of the delegate being removed
    event DelegatedAccountRemoved(
        address indexed caller, address indexed delegate
    );

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

    /// @dev sets owner to _owner and not msg.sender
    /// @param _owner The address of the owner
    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                                 VIEWS
    //////////////////////////////////////////////////////////////*/

    /// @return true if the caller is the owner
    function isOwner() public view virtual returns (bool) {
        return (msg.sender == owner);
    }

    /// @return true if the caller is the owner or a delegate
    function isAuth() public view virtual returns (bool) {
        return (msg.sender == owner || delegates[msg.sender]);
    }

    /*//////////////////////////////////////////////////////////////
                                SETTERS
    //////////////////////////////////////////////////////////////*/

    /// @notice Transfer ownership of the account
    /// @dev only owner can transfer ownership (not delegates)
    /// @param _newOwner The address of the new owner
    function transferOwnership(address _newOwner) public virtual {
        if (!isOwner()) revert Unauthorized();

        owner = _newOwner;

        emit OwnershipTransferred(msg.sender, _newOwner);
    }

    /// @notice Add a delegate to the account
    /// @dev only owner can add a delegate (not delegates)
    /// @param _delegate The address of the delegate
    function addDelegate(address _delegate) public virtual {
        if (!isOwner()) revert Unauthorized();

        if (_delegate == address(0) || delegates[_delegate]) {
            revert InvalidDelegateAddress(_delegate);
        }

        delegates[_delegate] = true;

        emit DelegatedAccountAdded({caller: msg.sender, delegate: _delegate});
    }

    /// @notice Remove a delegate from the account
    /// @dev only owner can remove a delegate (not delegates)
    /// @param _delegate The address of the delegate
    function removeDelegate(address _delegate) public virtual {
        if (!isOwner()) revert Unauthorized();

        if (_delegate == address(0) || !delegates[_delegate]) {
            revert InvalidDelegateAddress(_delegate);
        }

        delete delegates[_delegate];

        emit DelegatedAccountRemoved({caller: msg.sender, delegate: _delegate});
    }
}

File 3 of 19 : BytesLib.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import {Constants} from "src/utils/uniswap/Constants.sol";

library BytesLib {
    error SliceOutOfBounds();

    /// @notice Returns the address starting at byte 0
    /// @dev length and overflow checks must be carried out before calling
    /// @param _bytes The input bytes string to slice
    /// @return _address The address starting at byte 0
    function toAddress(bytes calldata _bytes)
        internal
        pure
        returns (address _address)
    {
        if (_bytes.length < Constants.ADDR_SIZE) revert SliceOutOfBounds();
        assembly {
            _address := shr(96, calldataload(_bytes.offset))
        }
    }

    /// @notice Returns the pool details starting at byte 0
    /// @dev length and overflow checks must be carried out before calling
    /// @param _bytes The input bytes string to slice
    /// @return token0 The address at byte 0
    /// @return fee The uint24 starting at byte 20
    /// @return token1 The address at byte 23
    function toPool(bytes calldata _bytes)
        internal
        pure
        returns (address token0, uint24 fee, address token1)
    {
        if (_bytes.length < Constants.V3_POP_OFFSET) revert SliceOutOfBounds();
        assembly {
            let firstWord := calldataload(_bytes.offset)
            token0 := shr(96, firstWord)
            fee := and(shr(72, firstWord), 0xffffff)
            token1 := shr(96, calldataload(add(_bytes.offset, 23)))
        }
    }

    /// @notice Decode the `_arg`-th element in `_bytes` as a dynamic array
    /// @dev The decoding of `length` and `offset` is universal,
    /// whereas the type declaration of `res` instructs the compiler how to read it.
    /// @param _bytes The input bytes string to slice
    /// @param _arg The index of the argument to extract
    /// @return length Length of the array
    /// @return offset Pointer to the data part of the array
    function toLengthOffset(bytes calldata _bytes, uint256 _arg)
        internal
        pure
        returns (uint256 length, uint256 offset)
    {
        uint256 relativeOffset;
        assembly {
            // The offset of the `_arg`-th element is `32 * arg`, which stores the offset of the length pointer.
            // shl(5, x) is equivalent to mul(32, x)
            let lengthPtr :=
                add(_bytes.offset, calldataload(add(_bytes.offset, shl(5, _arg))))
            length := calldataload(lengthPtr)
            offset := add(lengthPtr, 0x20)
            relativeOffset := sub(offset, _bytes.offset)
        }
        if (_bytes.length < length + relativeOffset) revert SliceOutOfBounds();
    }

    /// @notice Decode the `_arg`-th element in `_bytes` as `bytes`
    /// @param _bytes The input bytes string to extract a bytes string from
    /// @param _arg The index of the argument to extract
    function toBytes(bytes calldata _bytes, uint256 _arg)
        internal
        pure
        returns (bytes calldata res)
    {
        (uint256 length, uint256 offset) = toLengthOffset(_bytes, _arg);
        assembly {
            res.length := length
            res.offset := offset
        }
    }
}

File 4 of 19 : IAccount.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import {IPerpsV2MarketConsolidated} from
    "src/interfaces/synthetix/IPerpsV2MarketConsolidated.sol";

/// @title Kwenta Smart Margin Account v2.1.0 Implementation Interface
/// @author JaredBorders ([email protected]), JChiaramonte7 ([email protected])
interface IAccount {
    /*///////////////////////////////////////////////////////////////
                                Types
    ///////////////////////////////////////////////////////////////*/

    /// @notice Command Flags used to decode commands to execute
    /// @dev under the hood ACCOUNT_MODIFY_MARGIN = 0, ACCOUNT_WITHDRAW_ETH = 1
    enum Command {
        ACCOUNT_MODIFY_MARGIN, // 0
        ACCOUNT_WITHDRAW_ETH,
        PERPS_V2_MODIFY_MARGIN,
        PERPS_V2_WITHDRAW_ALL_MARGIN,
        PERPS_V2_SUBMIT_ATOMIC_ORDER,
        PERPS_V2_SUBMIT_DELAYED_ORDER, // 5
        PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER,
        PERPS_V2_CLOSE_POSITION,
        PERPS_V2_SUBMIT_CLOSE_DELAYED_ORDER,
        PERPS_V2_SUBMIT_CLOSE_OFFCHAIN_DELAYED_ORDER,
        PERPS_V2_CANCEL_DELAYED_ORDER, // 10
        PERPS_V2_CANCEL_OFFCHAIN_DELAYED_ORDER,
        GELATO_PLACE_CONDITIONAL_ORDER,
        GELATO_CANCEL_CONDITIONAL_ORDER,
        UNISWAP_V3_SWAP,
        PERMIT2_PERMIT // 15
    }

    /// @notice denotes conditional order types for code clarity
    /// @dev under the hood LIMIT = 0, STOP = 1
    enum ConditionalOrderTypes {
        LIMIT,
        STOP
    }

    /// @notice denotes conditional order cancelled reasons for code clarity
    /// @dev under the hood CONDITIONAL_ORDER_CANCELLED_BY_USER = 0, CONDITIONAL_ORDER_CANCELLED_NOT_REDUCE_ONLY = 1
    enum ConditionalOrderCancelledReason {
        CONDITIONAL_ORDER_CANCELLED_BY_USER,
        CONDITIONAL_ORDER_CANCELLED_NOT_REDUCE_ONLY
    }

    /// @notice denotes what oracle is used for price when executing conditional orders
    /// @dev under the hood PYTH = 0, CHAINLINK = 1
    enum PriceOracleUsed {
        PYTH,
        CHAINLINK
    }

    /// @param factory: address of the Smart Margin Account Factory
    /// @param events: address of the contract used by all accounts for emitting events
    /// @param marginAsset: address of the Synthetix ProxyERC20sUSD contract used as the margin asset
    /// @param perpsV2ExchangeRate: address of the Synthetix PerpsV2ExchangeRate
    /// @param futuresMarketManager: address of the Synthetix FuturesMarketManager
    /// @param systemStatus: address of the Synthetix SystemStatus
    /// @param gelato: address of Gelato
    /// @param ops: address of Ops
    /// @param settings: address of contract used to store global settings
    /// @param universalRouter: address of Uniswap's Universal Router
    /// @param permit2: address of Uniswap's Permit2
    struct AccountConstructorParams {
        address factory;
        address events;
        address marginAsset;
        address perpsV2ExchangeRate;
        address futuresMarketManager;
        address systemStatus;
        address gelato;
        address ops;
        address settings;
        address universalRouter;
        address permit2;
    }

    /// marketKey: Synthetix PerpsV2 Market id/key
    /// marginDelta: amount of margin to deposit or withdraw; positive indicates deposit, negative withdraw
    /// sizeDelta: denoted in market currency (i.e. ETH, BTC, etc), size of Synthetix PerpsV2 position
    /// targetPrice: limit or stop price target needing to be met to submit Synthetix PerpsV2 order
    /// gelatoTaskId: unqiue taskId from gelato necessary for cancelling conditional orders
    /// conditionalOrderType: conditional order type to determine conditional order fill logic
    /// desiredFillPrice: desired price to fill Synthetix PerpsV2 order at execution time
    /// reduceOnly: if true, only allows position's absolute size to decrease
    struct ConditionalOrder {
        bytes32 marketKey;
        int256 marginDelta;
        int256 sizeDelta;
        uint256 targetPrice;
        bytes32 gelatoTaskId;
        ConditionalOrderTypes conditionalOrderType;
        uint256 desiredFillPrice;
        bool reduceOnly;
    }
    /// @dev see example below elucidating targtPrice vs desiredFillPrice:
    /// 1. targetPrice met (ex: targetPrice = X)
    /// 2. account submits delayed order to Synthetix PerpsV2 with desiredFillPrice = Y
    /// 3. keeper executes Synthetix PerpsV2 order after delay period
    /// 4. if current market price defined by Synthetix PerpsV2
    ///    after delay period satisfies desiredFillPrice order is filled

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice thrown when commands length does not equal inputs length
    error LengthMismatch();

    /// @notice thrown when Command given is not valid
    error InvalidCommandType(uint256 commandType);

    /// @notice thrown when conditional order type given is not valid due to zero sizeDelta
    error ZeroSizeDelta();

    /// @notice exceeds useable margin
    /// @param available: amount of useable margin asset
    /// @param required: amount of margin asset required
    error InsufficientFreeMargin(uint256 available, uint256 required);

    /// @notice call to transfer ETH on withdrawal fails
    error EthWithdrawalFailed();

    /// @notice base price from the oracle was invalid
    /// @dev Rate can be invalid either due to:
    ///     1. Returned as invalid from ExchangeRates - due to being stale or flagged by oracle
    ///     2. Out of deviation bounds w.r.t. to previously stored rate
    ///     3. if there is no valid stored rate, w.r.t. to previous 3 oracle rates
    ///     4. Price is zero
    error InvalidPrice();

    /// @notice thrown when account execution has been disabled in the settings contract
    error AccountExecutionDisabled();

    /// @notice thrown when a call attempts to reenter the protected function
    error Reentrancy();

    /// @notice thrown when token swap attempted with invalid token (i.e. token that is not whitelisted)
    /// @param tokenIn: token attempting to swap from
    /// @param tokenOut: token attempting to swap to
    error TokenSwapNotAllowed(address tokenIn, address tokenOut);

    /// @notice thrown when a conditional order is attempted to be executed during invalid market conditions
    /// @param conditionalOrderId: conditional order id
    /// @param executor: address of executor
    error CannotExecuteConditionalOrder(
        uint256 conditionalOrderId, address executor
    );

    /// @notice thrown when a conditional order is attempted to be executed but SM account cannot pay fee
    /// @param executorFee: fee required to execute conditional order
    error CannotPayExecutorFee(uint256 executorFee, address executor);

    /*//////////////////////////////////////////////////////////////
                                 VIEWS
    //////////////////////////////////////////////////////////////*/

    /// @notice returns the version of the Account
    function VERSION() external view returns (bytes32);

    /// @return returns the amount of margin locked for future events (i.e. conditional orders)
    function committedMargin() external view returns (uint256);

    /// @return returns current conditional order id
    function conditionalOrderId() external view returns (uint256);

    /// @notice get delayed order data from Synthetix PerpsV2
    /// @dev call reverts if _marketKey is invalid
    /// @param _marketKey: key for Synthetix PerpsV2 Market
    /// @return delayed order struct defining delayed order (will return empty struct if no delayed order exists)
    function getDelayedOrder(bytes32 _marketKey)
        external
        returns (IPerpsV2MarketConsolidated.DelayedOrder memory);

    /// @notice checker() is the Resolver for Gelato
    /// (see https://docs.gelato.network/developer-services/automate/guides/custom-logic-triggers/smart-contract-resolvers)
    /// @notice signal to a keeper that a conditional order is valid/invalid for execution
    /// @dev call reverts if conditional order Id does not map to a valid conditional order;
    /// ConditionalOrder.marketKey would be invalid
    /// @param _conditionalOrderId: key for an active conditional order
    /// @return canExec boolean that signals to keeper a conditional order can be executed by Gelato
    /// @return execPayload calldata for executing a conditional order
    function checker(uint256 _conditionalOrderId)
        external
        view
        returns (bool canExec, bytes memory execPayload);

    /// @notice the current withdrawable or usable balance
    /// @return free margin amount
    function freeMargin() external view returns (uint256);

    /// @notice get up-to-date position data from Synthetix PerpsV2
    /// @param _marketKey: key for Synthetix PerpsV2 Market
    /// @return position struct defining current position
    function getPosition(bytes32 _marketKey)
        external
        returns (IPerpsV2MarketConsolidated.Position memory);

    /// @notice conditional order id mapped to conditional order
    /// @param _conditionalOrderId: id of conditional order
    /// @return conditional order
    function getConditionalOrder(uint256 _conditionalOrderId)
        external
        view
        returns (ConditionalOrder memory);

    /*//////////////////////////////////////////////////////////////
                                MUTATIVE
    //////////////////////////////////////////////////////////////*/

    /// @notice sets the initial owner of the account
    /// @dev only called once by the factory on account creation
    /// @param _owner: address of the owner
    function setInitialOwnership(address _owner) external;

    /// @notice executes commands along with provided inputs
    /// @param _commands: array of commands, each represented as an enum
    /// @param _inputs: array of byte strings containing abi encoded inputs for each command
    function execute(Command[] calldata _commands, bytes[] calldata _inputs)
        external
        payable;

    /// @notice execute queued conditional order
    /// @dev currently only supports conditional order submission via PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER COMMAND
    /// @param _conditionalOrderId: key for an active conditional order
    function executeConditionalOrder(uint256 _conditionalOrderId) external;
}

File 5 of 19 : IFactory.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/// @title Kwenta Factory Interface
/// @author JaredBorders ([email protected])
interface IFactory {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice emitted when new account is created
    /// @param creator: account creator (address that called newAccount())
    /// @param account: address of account that was created (will be address of proxy)
    /// @param version: version of account created
    event NewAccount(
        address indexed creator, address indexed account, bytes32 version
    );

    /// @notice emitted when implementation is upgraded
    /// @param implementation: address of new implementation
    event AccountImplementationUpgraded(address implementation);

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice thrown when factory cannot set account owner to the msg.sender
    /// @param data: data returned from failed low-level call
    error FailedToSetAcountOwner(bytes data);

    /// @notice thrown when Account creation fails due to no version being set
    /// @param data: data returned from failed low-level call
    error AccountFailedToFetchVersion(bytes data);

    /// @notice thrown when factory is not upgradable
    error CannotUpgrade();

    /// @notice thrown when account is unrecognized by factory
    error AccountDoesNotExist();

    /*//////////////////////////////////////////////////////////////
                                 VIEWS
    //////////////////////////////////////////////////////////////*/

    /// @return canUpgrade: bool to determine if system can be upgraded
    function canUpgrade() external view returns (bool);

    /// @return logic: account logic address
    function implementation() external view returns (address);

    /// @param _account: address of account
    /// @return whether or not account exists
    function accounts(address _account) external view returns (bool);

    /// @param _account: address of account
    /// @return owner of account
    function getAccountOwner(address _account)
        external
        view
        returns (address);

    /// @param _owner: address of owner
    /// @return array of accounts owned by _owner
    function getAccountsOwnedBy(address _owner)
        external
        view
        returns (address[] memory);

    /*//////////////////////////////////////////////////////////////
                               OWNERSHIP
    //////////////////////////////////////////////////////////////*/

    /// @notice update owner to account(s) mapping
    /// @dev does *NOT* check new owner != old owner
    /// @param _newOwner: new owner of account
    /// @param _oldOwner: old owner of account
    function updateAccountOwnership(address _newOwner, address _oldOwner)
        external;

    /*//////////////////////////////////////////////////////////////
                           ACCOUNT DEPLOYMENT
    //////////////////////////////////////////////////////////////*/

    /// @notice create unique account proxy for function caller
    /// @return accountAddress address of account created
    function newAccount() external returns (address payable accountAddress);

    /*//////////////////////////////////////////////////////////////
                             UPGRADABILITY
    //////////////////////////////////////////////////////////////*/

    /// @notice upgrade implementation of account which all account proxies currently point to
    /// @dev this *will* impact all existing accounts
    /// @dev future accounts will also point to this new implementation (until
    /// upgradeAccountImplementation() is called again with a newer implementation)
    /// @dev *DANGER* this function does not check the new implementation for validity,
    /// thus, a bad upgrade could result in severe consequences.
    /// @param _implementation: address of new implementation
    function upgradeAccountImplementation(address _implementation) external;

    /// @notice remove upgradability from factory
    /// @dev cannot be undone
    function removeUpgradability() external;
}

File 6 of 19 : IFuturesMarketManager.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

interface IFuturesMarketManager {
    function marketForKey(bytes32 marketKey) external view returns (address);
}

File 7 of 19 : IPermit2.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

interface IPermit2 {
    /// @notice The permit data for a token
    struct PermitDetails {
        // ERC20 token address
        address token;
        // the maximum amount allowed to spend
        uint160 amount;
        // timestamp at which a spender's token allowances become invalid
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each signature
        uint48 nonce;
    }

    /// @notice The permit message signed for a single token allownce
    struct PermitSingle {
        // the permit data for a single token alownce
        PermitDetails details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }

    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @dev Should return whether the signature provided is valid for the provided data
    /// @param hash      Hash of the data to be signed
    /// @param signature Signature byte array associated with _data
    /// @return magicValue The bytes4 magic value 0x1626ba7e
    function isValidSignature(bytes32 hash, bytes memory signature)
        external
        view
        returns (bytes4 magicValue);

    /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
    /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
    /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
    function allowance(address user, address token, address spender)
        external
        view
        returns (uint160 amount, uint48 expiration, uint48 nonce);

    /// @notice Approves the spender to use up to amount of the specified token up until the expiration
    /// @param token The token to approve
    /// @param spender The spender address to approve
    /// @param amount The approved amount of the token
    /// @param expiration The timestamp at which the approval is no longer valid
    /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
    /// @dev Setting amount to type(uint160).max sets an unlimited approval
    function approve(
        address token,
        address spender,
        uint160 amount,
        uint48 expiration
    ) external;

    /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
    /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
    /// @param owner The owner of the tokens being approved
    /// @param permitSingle Data signed over by the owner specifying the terms of approval
    /// @param signature The owner's signature over the permit data
    function permit(
        address owner,
        PermitSingle memory permitSingle,
        bytes calldata signature
    ) external;

    /// @notice Transfer approved tokens from one address to another
    /// @param from The address to transfer from
    /// @param to The address of the recipient
    /// @param amount The amount of the token to transfer
    /// @param token The token address to transfer
    /// @dev Requires the from address to have approved at least the desired amount
    /// of tokens to msg.sender.
    function transferFrom(
        address from,
        address to,
        uint160 amount,
        address token
    ) external;
}

File 8 of 19 : ISettings.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/// @title Kwenta Smart Margin Account Settings Interface
/// @author JaredBorders ([email protected])
interface ISettings {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice emitted when account execution is enabled or disabled
    /// @param enabled: true if account execution is enabled, false if disabled
    event AccountExecutionEnabledSet(bool enabled);

    /// @notice emitted when the executor fee is updated
    /// @param executorFee: the executor fee
    event ExecutorFeeSet(uint256 executorFee);

    /// @notice emitted when a token is added to or removed from the whitelist
    /// @param token: address of the token
    /// @param isWhitelisted: true if token is whitelisted, false if not
    event TokenWhitelistStatusUpdated(address token, bool isWhitelisted);

    /*//////////////////////////////////////////////////////////////
                                 VIEWS
    //////////////////////////////////////////////////////////////*/

    /// @notice checks if account execution is enabled or disabled
    /// @return enabled: true if account execution is enabled, false if disabled
    function accountExecutionEnabled() external view returns (bool);

    /// @notice gets the conditional order executor fee
    /// @return executorFee: the executor fee
    function executorFee() external view returns (uint256);

    /// @notice checks if token is whitelisted
    /// @param _token: address of the token to check
    /// @return true if token is whitelisted, false if not
    function isTokenWhitelisted(address _token) external view returns (bool);

    /*//////////////////////////////////////////////////////////////
                                SETTERS
    //////////////////////////////////////////////////////////////*/

    /// @notice enables or disables account execution
    /// @param _enabled: true if account execution is enabled, false if disabled
    function setAccountExecutionEnabled(bool _enabled) external;

    /// @notice sets the conditional order executor fee
    /// @param _executorFee: the executor fee
    function setExecutorFee(uint256 _executorFee) external;

    /// @notice adds/removes token to/from whitelist
    /// @dev does not check if token was previously whitelisted
    /// @param _token: address of the token to add
    /// @param _isWhitelisted: true if token is to be whitelisted, false if not
    function setTokenWhitelistStatus(address _token, bool _isWhitelisted)
        external;
}

File 9 of 19 : ISystemStatus.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

interface ISystemStatus {
    function requireFuturesMarketActive(bytes32 marketKey) external view;
}

File 10 of 19 : IOps.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

interface IOps {
    /**
     * @notice Whitelisted modules that are available for users to customise conditions and specifications of their tasks.
     *
     * @param RESOLVER Use dynamic condition & input data for execution. {See ResolverModule.sol}
     * @param TIME Repeated execution of task at a specified timing and interval. {See TimeModule.sol}
     * @param PROXY Creates a dedicated caller (msg.sender) to be used when executing the task. {See ProxyModule.sol}
     * @param SINGLE_EXEC Task is cancelled after one execution. {See SingleExecModule.sol}
     */
    enum Module {
        RESOLVER,
        TIME,
        PROXY,
        SINGLE_EXEC
    }

    /**
     * @notice Struct to contain modules and their relative arguments that are used for task creation.
     *
     * @param modules List of selected modules.
     * @param args Arguments of modules if any. Pass "0x" for modules which does not require args {See encodeModuleArg}
     */
    struct ModuleData {
        Module[] modules;
        bytes[] args;
    }

    /**
     * @notice Struct for time module.
     *
     * @param nextExec Time when the next execution should occur.
     * @param interval Time interval between each execution.
     */
    struct Time {
        uint128 nextExec;
        uint128 interval;
    }

    /**
     * @notice Initiates a task with conditions which Gelato will monitor and execute when conditions are met.
     *
     * @param execAddress Address of contract that should be called by Gelato.
     * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
     * @param moduleData Conditional modules that will be used.
     * @param feeToken Address of token to be used as payment. Use address(0) if TaskTreasury is being used, 0xeeeeee... for ETH or native tokens.
     *
     * @return taskId Unique hash of the task created.
     */
    function createTask(
        address execAddress,
        bytes calldata execData,
        ModuleData calldata moduleData,
        address feeToken
    ) external returns (bytes32 taskId);

    /**
     * @notice Terminates a task that was created and Gelato can no longer execute it.
     *
     * @param taskId Unique hash of the task that is being cancelled. {See LibTaskId-getTaskId}
     */
    function cancelTask(bytes32 taskId) external;

    /**
     * @notice Execution API called by Gelato.
     *
     * @param taskCreator The address which created the task.
     * @param execAddress Address of contract that should be called by Gelato.
     * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
     * @param moduleData Conditional modules that will be used.
     * @param txFee Fee paid to Gelato for execution, deducted on the TaskTreasury or transfered to Gelato.
     * @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
     * @param useTaskTreasuryFunds If taskCreator's balance on TaskTreasury should pay for the tx.
     * @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
     */
    function exec(
        address taskCreator,
        address execAddress,
        bytes memory execData,
        ModuleData calldata moduleData,
        uint256 txFee,
        address feeToken,
        bool useTaskTreasuryFunds,
        bool revertOnFailure
    ) external;

    /**
     * @notice Sets the address of task modules. Only callable by proxy admin.
     *
     * @param modules List of modules to be set
     * @param moduleAddresses List of addresses for respective modules.
     */
    function setModule(
        Module[] calldata modules,
        address[] calldata moduleAddresses
    ) external;

    /**
     * @notice Helper function to query fee and feeToken to be used for payment. (For executions which pays itself)
     *
     * @return uint256 Fee amount to be paid.
     * @return address Token to be paid. (Determined and passed by taskCreator during createTask)
     */
    function getFeeDetails() external view returns (uint256, address);

    /**
     * @notice Helper func to query all open tasks by a task creator.
     *
     * @param taskCreator Address of task creator to query.
     *
     * @return bytes32[] List of taskIds created.
     */
    function getTaskIdsByUser(address taskCreator)
        external
        view
        returns (bytes32[] memory);

    /**
     * @notice Helper function to compute task id with module arguments
     *
     * @param taskCreator The address which created the task.
     * @param execAddress Address of contract that will be called by Gelato.
     * @param execSelector Signature of the function which will be called by Gelato.
     * @param moduleData  Conditional modules that will be used. {See LibDataTypes-ModuleData}
     * @param feeToken Address of token to be used as payment. Use address(0) if TaskTreasury is being used, 0xeeeeee... for ETH or native tokens.
     */
    function getTaskId(
        address taskCreator,
        address execAddress,
        bytes4 execSelector,
        ModuleData memory moduleData,
        address feeToken
    ) external pure returns (bytes32 taskId);
}

File 11 of 19 : IUniversalRouter.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

interface IUniversalRouter {
    /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.
    /// @param commands A set of concatenated commands, each 1 byte in length
    /// @param inputs An array of byte strings containing abi encoded inputs for each command
    /// @param deadline The deadline by which the transaction must be executed
    function execute(
        bytes calldata commands,
        bytes[] calldata inputs,
        uint256 deadline
    ) external payable;

    /// @notice Executes encoded commands along with provided inputs
    /// @param commands A set of concatenated commands, each 1 byte in length
    /// @param inputs An array of byte strings containing abi encoded inputs for each command
    function execute(bytes calldata commands, bytes[] calldata inputs)
        external
        payable;
}

File 12 of 19 : IEvents.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

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

/// @title Interface for contract that emits all events emitted by the Smart Margin Accounts
/// @author JaredBorders ([email protected])
interface IEvents {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice emitted when a non-account contract attempts to call a restricted function
    error OnlyAccounts();

    /*//////////////////////////////////////////////////////////////
                                 VIEWS
    //////////////////////////////////////////////////////////////*/

    /// @notice returns the address of the factory contract
    function factory() external view returns (address);

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice emitted after a successful withdrawal
    /// @param user: the address that withdrew from account
    /// @param amount: amount of marginAsset to withdraw from account
    function emitDeposit(address user, uint256 amount) external;

    event Deposit(
        address indexed user, address indexed account, uint256 amount
    );

    /// @notice emitted after a successful withdrawal
    /// @param user: the address that withdrew from account
    /// @param amount: amount of marginAsset to withdraw from account
    function emitWithdraw(address user, uint256 amount) external;

    event Withdraw(
        address indexed user, address indexed account, uint256 amount
    );

    /// @notice emitted after a successful ETH withdrawal
    /// @param user: the address that withdrew from account
    /// @param amount: amount of ETH to withdraw from account
    function emitEthWithdraw(address user, uint256 amount) external;

    event EthWithdraw(
        address indexed user, address indexed account, uint256 amount
    );

    /// @notice emitted after a successful token swap
    /// @param tokenIn: contract address of the inbound token
    /// @param tokenOut: contract address of the outbound token
    /// @param recipient: address to receive the outbound token
    /// @param amountIn: amount of inbound token to swap
    /// @param amountOutMinimum: minimum amount of outbound token to receive
    function emitUniswapV3Swap(
        address tokenIn,
        address tokenOut,
        address recipient,
        uint256 amountIn,
        uint256 amountOutMinimum
    ) external;

    event UniswapV3Swap(
        address tokenIn,
        address tokenOut,
        address recipient,
        uint256 amountIn,
        uint256 amountOutMinimum
    );

    /// @notice emitted when a conditional order is placed
    /// @param conditionalOrderId: id of conditional order
    /// @param gelatoTaskId: id of gelato task
    /// @param marketKey: Synthetix PerpsV2 market key
    /// @param marginDelta: margin change
    /// @param sizeDelta: size change
    /// @param targetPrice: targeted fill price
    /// @param conditionalOrderType: expected conditional order type enum where 0 = LIMIT, 1 = STOP, etc..
    /// @param desiredFillPrice: desired price to fill Synthetix PerpsV2 order at execution time
    /// @param reduceOnly: if true, only allows position's absolute size to decrease
    function emitConditionalOrderPlaced(
        uint256 conditionalOrderId,
        bytes32 gelatoTaskId,
        bytes32 marketKey,
        int256 marginDelta,
        int256 sizeDelta,
        uint256 targetPrice,
        IAccount.ConditionalOrderTypes conditionalOrderType,
        uint256 desiredFillPrice,
        bool reduceOnly
    ) external;

    event ConditionalOrderPlaced(
        address indexed account,
        uint256 indexed conditionalOrderId,
        bytes32 indexed gelatoTaskId,
        bytes32 marketKey,
        int256 marginDelta,
        int256 sizeDelta,
        uint256 targetPrice,
        IAccount.ConditionalOrderTypes conditionalOrderType,
        uint256 desiredFillPrice,
        bool reduceOnly
    );

    /// @notice emitted when a conditional order is cancelled
    /// @param conditionalOrderId: id of conditional order
    /// @param gelatoTaskId: id of gelato task
    /// @param reason: reason for cancellation
    function emitConditionalOrderCancelled(
        uint256 conditionalOrderId,
        bytes32 gelatoTaskId,
        IAccount.ConditionalOrderCancelledReason reason
    ) external;

    event ConditionalOrderCancelled(
        address indexed account,
        uint256 indexed conditionalOrderId,
        bytes32 indexed gelatoTaskId,
        IAccount.ConditionalOrderCancelledReason reason
    );

    /// @notice emitted when a conditional order is filled
    /// @param conditionalOrderId: id of conditional order
    /// @param gelatoTaskId: id of gelato task
    /// @param fillPrice: price the conditional order was executed at
    /// @param keeperFee: fees paid to the executor
    /// @param priceOracle: price oracle used to execute conditional order
    function emitConditionalOrderFilled(
        uint256 conditionalOrderId,
        bytes32 gelatoTaskId,
        uint256 fillPrice,
        uint256 keeperFee,
        IAccount.PriceOracleUsed priceOracle
    ) external;

    event ConditionalOrderFilled(
        address indexed account,
        uint256 indexed conditionalOrderId,
        bytes32 indexed gelatoTaskId,
        uint256 fillPrice,
        uint256 keeperFee,
        IAccount.PriceOracleUsed priceOracle
    );
}

File 13 of 19 : IPerpsV2ExchangeRate.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/// Used to fetch a price with a degree of uncertainty, represented as a price +- a confidence interval.
///
/// The confidence interval roughly corresponds to the standard error of a normal distribution.
/// Both the price and confidence are stored in a fixed-point numeric representation,
/// `x * (10^expo)`, where `expo` is the exponent.
///
/// Please refer to the documentation at https://docs.pyth.network/consumers/best-practices for how
/// to how this price safely.
interface IPerpsV2ExchangeRate {
    /// @notice Returns the price of a price feed without any sanity checks.
    /// @dev This function returns the most recent price update in this contract without any recency checks.
    /// This function is unsafe as the returned price update may be arbitrarily far in the past.
    ///
    /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
    /// sufficiently recent for their application. If you are considering using this function, it may be
    /// safer / easier to use either `getPrice` or `getPriceNoOlderThan`.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    /// @return publishTime - unix timestamp describing when the price was published.
    function resolveAndGetLatestPrice(bytes32 assetId)
        external
        view
        returns (uint256 price, uint256 publishTime);
}

File 14 of 19 : OpsReady.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/// @dev Inherit this contract to allow your smart
/// contract to make synchronous fee payments and have
/// call restrictions for functions to be automated.
abstract contract OpsReady {
    /// @notice address of Gelato Network contract
    address public immutable GELATO;

    /// @notice address of Gelato `Automate` contract
    address public immutable OPS;

    /// @notice internal address representation of ETH (used by Gelato)
    address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /// @notice sets the addresses of the Gelato Network contracts
    /// @param _gelato: address of the Gelato Network contract
    /// @param _ops: address of the Gelato `Automate` contract
    constructor(address _gelato, address _ops) {
        GELATO = _gelato;
        OPS = _ops;
    }

    /// @notice transfers fee (in ETH) to gelato for synchronous fee payments
    /// @dev happens at task execution time
    /// @param _amount: amount of asset to transfer
    function _transfer(uint256 _amount) internal {
        (bool success,) = GELATO.call{value: _amount}("");
        require(success, "OpsReady: ETH transfer failed");
    }
}

File 15 of 19 : SafeCast160.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

library SafeCast160 {
    /// @notice Thrown when a valude greater than type(uint160).max is cast to uint160
    error UnsafeCast();

    /// @notice Safely casts uint256 to uint160
    /// @param value The uint256 to be cast
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) revert UnsafeCast();
        return uint160(value);
    }
}

File 16 of 19 : IERC20.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/**
 * @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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

File 17 of 19 : V3Path.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import {BytesLib} from "src/utils/uniswap/BytesLib.sol";
import {Constants} from "src/utils/uniswap/Constants.sol";

/// @title Functions for manipulating path data for multihop swaps
library V3Path {
    using BytesLib for bytes;

    /// @notice Returns true iff the path contains two or more pools
    /// @param path The encoded swap path
    /// @return True if path contains two or more pools, otherwise false
    function hasMultiplePools(bytes calldata path)
        internal
        pure
        returns (bool)
    {
        return path.length >= Constants.MULTIPLE_V3_POOLS_MIN_LENGTH;
    }

    function decodeFirstToken(bytes calldata path)
        internal
        pure
        returns (address tokenA)
    {
        tokenA = path.toAddress();
    }

    /// @notice Skips a token + fee element
    /// @param path The swap path
    function skipToken(bytes calldata path)
        internal
        pure
        returns (bytes calldata)
    {
        return path[Constants.NEXT_V3_POOL_OFFSET:];
    }
}

File 18 of 19 : Constants.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/// @title Constant state
/// @notice Constant state used by the Universal Router
library Constants {
    /// @dev The length of the bytes encoded address
    uint256 internal constant ADDR_SIZE = 20;

    /// @dev The length of the bytes encoded fee
    uint256 internal constant V3_FEE_SIZE = 3;

    /// @dev The offset of a single token address (20) and pool fee (3)
    uint256 internal constant NEXT_V3_POOL_OFFSET = ADDR_SIZE + V3_FEE_SIZE;

    /// @dev The offset of an encoded pool key
    /// Token (20) + Fee (3) + Token (20) = 43
    uint256 internal constant V3_POP_OFFSET = NEXT_V3_POOL_OFFSET + ADDR_SIZE;

    /// @dev The minimum length of an encoding that contains 2 or more pools
    uint256 internal constant MULTIPLE_V3_POOLS_MIN_LENGTH =
        V3_POP_OFFSET + NEXT_V3_POOL_OFFSET;
}

File 19 of 19 : IPerpsV2MarketConsolidated.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

interface IPerpsV2MarketConsolidated {
    struct Position {
        uint64 id;
        uint64 lastFundingIndex;
        uint128 margin;
        uint128 lastPrice;
        int128 size;
    }

    struct DelayedOrder {
        bool isOffchain;
        int128 sizeDelta;
        uint128 desiredFillPrice;
        uint128 targetRoundId;
        uint128 commitDeposit;
        uint128 keeperDeposit;
        uint256 executableAtTime;
        uint256 intentionTime;
        bytes32 trackingCode;
    }

    function marketKey() external view returns (bytes32 key);

    function positions(address account)
        external
        view
        returns (Position memory);

    function delayedOrders(address account)
        external
        view
        returns (DelayedOrder memory);

    function baseAsset() external view returns (bytes32 key);

    function assetPrice() external view returns (uint256 price, bool invalid);

    function transferMargin(int256 marginDelta) external;

    function withdrawAllMargin() external;

    function modifyPositionWithTracking(
        int256 sizeDelta,
        uint256 desiredFillPrice,
        bytes32 trackingCode
    ) external;

    function closePositionWithTracking(
        uint256 desiredFillPrice,
        bytes32 trackingCode
    ) external;

    function submitCloseOffchainDelayedOrderWithTracking(
        uint256 desiredFillPrice,
        bytes32 trackingCode
    ) external;

    function submitCloseDelayedOrderWithTracking(
        uint256 desiredTimeDelta,
        uint256 desiredFillPrice,
        bytes32 trackingCode
    ) external;

    function submitDelayedOrderWithTracking(
        int256 sizeDelta,
        uint256 desiredTimeDelta,
        uint256 desiredFillPrice,
        bytes32 trackingCode
    ) external;

    function submitOffchainDelayedOrderWithTracking(
        int256 sizeDelta,
        uint256 desiredFillPrice,
        bytes32 trackingCode
    ) external;

    function cancelDelayedOrder(address account) external;

    function cancelOffchainDelayedOrder(address account) external;
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"address","name":"factory","type":"address"},{"internalType":"address","name":"events","type":"address"},{"internalType":"address","name":"marginAsset","type":"address"},{"internalType":"address","name":"perpsV2ExchangeRate","type":"address"},{"internalType":"address","name":"futuresMarketManager","type":"address"},{"internalType":"address","name":"systemStatus","type":"address"},{"internalType":"address","name":"gelato","type":"address"},{"internalType":"address","name":"ops","type":"address"},{"internalType":"address","name":"settings","type":"address"},{"internalType":"address","name":"universalRouter","type":"address"},{"internalType":"address","name":"permit2","type":"address"}],"internalType":"struct IAccount.AccountConstructorParams","name":"_params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountExecutionDisabled","type":"error"},{"inputs":[{"internalType":"uint256","name":"conditionalOrderId","type":"uint256"},{"internalType":"address","name":"executor","type":"address"}],"name":"CannotExecuteConditionalOrder","type":"error"},{"inputs":[{"internalType":"uint256","name":"executorFee","type":"uint256"},{"internalType":"address","name":"executor","type":"address"}],"name":"CannotPayExecutorFee","type":"error"},{"inputs":[],"name":"EthWithdrawalFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InsufficientFreeMargin","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[{"internalType":"address","name":"delegateAddress","type":"address"}],"name":"InvalidDelegateAddress","type":"error"},{"inputs":[],"name":"InvalidPrice","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"TokenSwapNotAllowed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnsafeCast","type":"error"},{"inputs":[],"name":"ZeroSizeDelta","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"delegate","type":"address"}],"name":"DelegatedAccountAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"delegate","type":"address"}],"name":"DelegatedAccountRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"ETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GELATO","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"contract IPermit2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"}],"name":"addDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_conditionalOrderId","type":"uint256"}],"name":"checker","outputs":[{"internalType":"bool","name":"canExec","type":"bool"},{"internalType":"bytes","name":"execPayload","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"committedMargin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"conditionalOrderId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegate","type":"address"}],"name":"delegates","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IAccount.Command[]","name":"_commands","type":"uint8[]"},{"internalType":"bytes[]","name":"_inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_conditionalOrderId","type":"uint256"}],"name":"executeConditionalOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freeMargin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_conditionalOrderId","type":"uint256"}],"name":"getConditionalOrder","outputs":[{"components":[{"internalType":"bytes32","name":"marketKey","type":"bytes32"},{"internalType":"int256","name":"marginDelta","type":"int256"},{"internalType":"int256","name":"sizeDelta","type":"int256"},{"internalType":"uint256","name":"targetPrice","type":"uint256"},{"internalType":"bytes32","name":"gelatoTaskId","type":"bytes32"},{"internalType":"enum IAccount.ConditionalOrderTypes","name":"conditionalOrderType","type":"uint8"},{"internalType":"uint256","name":"desiredFillPrice","type":"uint256"},{"internalType":"bool","name":"reduceOnly","type":"bool"}],"internalType":"struct IAccount.ConditionalOrder","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_marketKey","type":"bytes32"}],"name":"getDelayedOrder","outputs":[{"components":[{"internalType":"bool","name":"isOffchain","type":"bool"},{"internalType":"int128","name":"sizeDelta","type":"int128"},{"internalType":"uint128","name":"desiredFillPrice","type":"uint128"},{"internalType":"uint128","name":"targetRoundId","type":"uint128"},{"internalType":"uint128","name":"commitDeposit","type":"uint128"},{"internalType":"uint128","name":"keeperDeposit","type":"uint128"},{"internalType":"uint256","name":"executableAtTime","type":"uint256"},{"internalType":"uint256","name":"intentionTime","type":"uint256"},{"internalType":"bytes32","name":"trackingCode","type":"bytes32"}],"internalType":"struct IPerpsV2MarketConsolidated.DelayedOrder","name":"order","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_marketKey","type":"bytes32"}],"name":"getPosition","outputs":[{"components":[{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"uint64","name":"lastFundingIndex","type":"uint64"},{"internalType":"uint128","name":"margin","type":"uint128"},{"internalType":"uint128","name":"lastPrice","type":"uint128"},{"internalType":"int128","name":"size","type":"int128"}],"internalType":"struct IPerpsV2MarketConsolidated.Position","name":"position","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isAuth","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"}],"name":"removeDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setInitialOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101e06040523480156200001257600080fd5b506040516200498a3803806200498a83398101604081905262000035916200014c565b60c081015160e0820151600080546001600160a01b0319168155604051819081907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001600160a01b03918216608090815290821660a09081528351831660c0526020840151831660e0526040840151831661010090815260608501518416610120908152928501518416610140908152918501518416610160528401518316610180529083015182166101a05290910151166101c0526200023f565b60405161016081016001600160401b03811182821017156200012957634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200014757600080fd5b919050565b600061016082840312156200016057600080fd5b6200016a620000f7565b62000175836200012f565b815262000185602084016200012f565b602082015262000198604084016200012f565b6040820152620001ab606084016200012f565b6060820152620001be608084016200012f565b6080820152620001d160a084016200012f565b60a0820152620001e460c084016200012f565b60c0820152620001f760e084016200012f565b60e08201526101006200020c8185016200012f565b90820152610120620002208482016200012f565b90820152610140620002348482016200012f565b908201529392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c05161460e6200037c60003960008181610332015281816117460152612892015260008181612700015281816128cf01526135a401526000818161068101528181610b7a01528181611c2501528181612649015261282001526000611a580152600061152801526000611dbf01526000818161083b015281816121810152818161232f015281816125b00152612787015260008181610ed30152818161101401528181612269015281816124350152818161253901528181612a5601528181612ffe015261323e015260008181611218015261149601526000818161022b01528181610d3401528181611b6001528181611b880152818161311d01526136910152600081816104fb01526132cc015261460e6000f3fe6080604052600436106101795760003560e01c806385652fec116100cb578063cdf8972e1161007f578063eff557a711610059578063eff557a7146104e9578063f2fde38b1461051d578063ffa1ad741461053d57600080fd5b8063cdf8972e1461047c578063d6c7ef8b146104a9578063e71bdf41146104c957600080fd5b80638f32d59b116100b05780638f32d59b1461040b57806394e05b2c14610438578063a48903371461046657600080fd5b806385652fec146103be5780638da5cb5b146103de57600080fd5b8063613838051161012d57806372a69c0d1161010757806372a69c0d1461035457806378e91014146103695780638322fff21461039657600080fd5b806361383805146102eb57806367e7646f146103005780636afdd8501461032057600080fd5b80634c2eefb41161015e5780634c2eefb414610272578063587cde1e14610296578063608d60eb146102d657600080fd5b80631928b3cb14610185578063472d04471461021957600080fd5b3661018057005b600080fd5b34801561019157600080fd5b506101a56101a0366004613a7c565b610571565b6040516102109190600060a08201905067ffffffffffffffff8084511683528060208501511660208401525060408301516fffffffffffffffffffffffffffffffff808216604085015280606086015116606085015250506080830151600f0b608083015292915050565b60405180910390f35b34801561022557600080fd5b5061024d7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610210565b34801561027e57600080fd5b5061028860165481565b604051908152602001610210565b3480156102a257600080fd5b506102c66102b1366004613ab7565b60016020526000908152604090205460ff1681565b6040519015158152602001610210565b6102e96102e4366004613b19565b61063b565b005b3480156102f757600080fd5b506102886107f1565b34801561030c57600080fd5b506102e961031b366004613ab7565b6108b5565b34801561032c57600080fd5b5061024d7f000000000000000000000000000000000000000000000000000000000000000081565b34801561036057600080fd5b506102c6610a18565b34801561037557600080fd5b50610389610384366004613a7c565b610a51565b6040516102109190613b85565b3480156103a257600080fd5b5061024d73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b3480156103ca57600080fd5b506102e96103d9366004613a7c565b610b34565b3480156103ea57600080fd5b5060005461024d9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561041757600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff1633146102c6565b34801561044457600080fd5b50610458610453366004613a7c565b61108f565b604051610210929190613cbb565b34801561047257600080fd5b5061028860155481565b34801561048857600080fd5b5061049c610497366004613a7c565b611116565b6040516102109190613d15565b3480156104b557600080fd5b506102e96104c4366004613ab7565b611200565b3480156104d557600080fd5b506102e96104e4366004613ab7565b6112df565b3480156104f557600080fd5b5061024d7f000000000000000000000000000000000000000000000000000000000000000081565b34801561052957600080fd5b506102e9610538366004613ab7565b611442565b34801561054957600080fd5b506102887f322e312e3000000000000000000000000000000000000000000000000000000081565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526105a5826114f6565b6040517f55f5751000000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906355f575109060240160a060405180830381865afa158015610611573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106359190613e16565b92915050565b601854600203610677576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60026018819055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663125127b36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070e9190613edb565b610744576040517f274ccc0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8281811461077e576040517fff633a3800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156107e4576107dc86868381811061079e5761079e613ef6565b90506020020160208101906107b39190613f25565b8585848181106107c5576107c5613ef6565b90506020028101906107d79190613f46565b6115d2565b600101610781565b5050600160185550505050565b6015546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000919073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610882573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a69190613fab565b6108b09190613ff3565b905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314610906576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116158061094f575073ffffffffffffffffffffffffffffffffffffffff811660009081526001602052604090205460ff16155b156109a3576040517fa3653d9f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811660008181526001602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555133917faab1e52e5e9244a59f6d721ebad6da39111d47dba7c5b9cd735a06f839e8eb0291a350565b6000805473ffffffffffffffffffffffffffffffffffffffff163314806108b05750503360009081526001602052604090205460ff1690565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810191909152610aa3826114f6565b6040517fc8b809aa00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff919091169063c8b809aa9060240161012060405180830381865afa158015610b10573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106359190614006565b601854600203610b70576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60026018819055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663125127b36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610be3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c079190613edb565b610c3d576040517f274ccc0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c4681611a08565b610c85576040517f4316b3d50000000000000000000000000000000000000000000000000000000081526004810182905233602482015260440161099a565b6000610c9082611116565b60008381526017602052604080822082815560018101839055600281018390556003810183905560048082018490556005820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116909155600683019490945560079091018054909316909255608083015190517fee8ca3b5000000000000000000000000000000000000000000000000000000008152918201529091507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063ee8ca3b590602401600060405180830381600087803b158015610d8d57600080fd5b505af1158015610da1573d6000803e3d6000fd5b505050506000610daf611b47565b90506000610dc083600001516114f6565b9050600080610dce83611d44565b915091508460e0015115610f7c576040517f55f5751000000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8516906355f575109060240160a060405180830381865afa158015610e49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6d9190613e16565b60800151600f0b9050801580610e8c5750610e8c818760400151611f30565b15610f485760808601516040517fd63f1ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169163d63f1ca691610f0b918b916001906004016140ab565b600060405180830381600087803b158015610f2557600080fd5b505af1158015610f39573d6000803e3d6000fd5b50505050505050505050611087565b60ff81901c60000380820118604087015160ff81901c600003908101181115610f7a57610f74816140d0565b60408701525b505b600085602001511315610fb157602085015160ff81901c6000039081011860156000828254610fab9190613ff3565b90915550505b610fbf838660200151611f56565b610fd28386604001518760c00151611fed565b60808501516040517f568824dc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169163568824dc9161104f918a9187908a908890600401614108565b600060405180830381600087803b15801561106957600080fd5b505af115801561107d573d6000803e3d6000fd5b5050505050505050505b506001601855565b6000606061109c83611a08565b91503073ffffffffffffffffffffffffffffffffffffffff166385652fec846040516024016110cd91815260200190565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050915091565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810191909152601760008381526020019081526020016000206040518061010001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820160009054906101000a900460ff1660018111156111cc576111cc613cd6565b60018111156111dd576111dd613cd6565b81526006820154602082015260079091015460ff16151560409091015292915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461126f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b60005473ffffffffffffffffffffffffffffffffffffffff163314611330576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81161580611378575073ffffffffffffffffffffffffffffffffffffffff811660009081526001602052604090205460ff165b156113c7576040517fa3653d9f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240161099a565b73ffffffffffffffffffffffffffffffffffffffff8116600081815260016020819052604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169092179091555133917f52d2da59f56f31503d90a3f1f08001c5705116f9e44f9ee08a7ebe1354b2cf4891a350565b61144b816120a0565b6040517f2e9ca20400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301523360248301527f00000000000000000000000000000000000000000000000000000000000000001690632e9ca204906044015b600060405180830381600087803b1580156114db57600080fd5b505af11580156114ef573d6000803e3d6000fd5b5050505050565b6040517fe63bfadb000000000000000000000000000000000000000000000000000000008152600481018290526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e63bfadb90602401602060405180830381865afa158015611584573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a8919061413e565b905073ffffffffffffffffffffffffffffffffffffffff81166115cd576115cd61415b565b919050565b600083600f8111156115e6576115e6613cd6565b905060028110806115f7575080600e145b80611602575080600f145b156117bb5760005473ffffffffffffffffffffffffffffffffffffffff163314611658576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084600f81111561166c5761166c613cd6565b0361168257823561167c81612161565b50611a02565b600184600f81111561169657611696613cd6565b036116a657823561167c8161247c565b600e84600f8111156116ba576116ba613cd6565b036116f45760008036816116d08787600261257d565b9150915086359350602087013592506116eb8484848461259b565b50505050611a02565b823660006117048386600661257d565b6040517f2b67b570000000000000000000000000000000000000000000000000000000008152919350915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632b67b570906117819033908790879087906004016141e9565b600060405180830381600087803b15801561179b57600080fd5b505af11580156117af573d6000803e3d6000fd5b50505050505050611a02565b6117c3610a18565b6117f9576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481101561183e57600284600f81111561181657611816613cd6565b03611833578235602084013561182c8282611f56565b5050611a02565b823561167c81612abb565b600681101561189c57600484600f81111561185b5761185b613cd6565b0361187f57823560208401356040850135611877838383612b03565b505050611a02565b82356020840135604085013560608601356116eb84848484612b83565b60088110156118e657600684600f8111156118b9576118b9613cd6565b036118d557823560208401356040850135611877838383611fed565b8235602084013561182c8282612c3d565b600a81101561193057600884600f81111561190357611903613cd6565b0361191f57823560208401356040850135611877838383612cb6565b8235602084013561182c8282612d36565b600c81101561196857600a84600f81111561194d5761194d613cd6565b0361195d57823561167c81612daf565b823561167c81612e01565b600e8110156119c457600c84600f81111561198557611985613cd6565b036119b9578235602084013560408501356060860135608087013560a088013560c08901356117af87878787878787612e53565b823561167c816130a6565b600f811115611a02576040517fd76a1e9e0000000000000000000000000000000000000000000000000000000081526004810182905260240161099a565b50505050565b600080611a1483611116565b8051909150611a265750600092915050565b80516040517f856aae6c00000000000000000000000000000000000000000000000000000000815260048101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063856aae6c9060240160006040518083038186803b158015611aaf57600080fd5b505afa925050508015611ac0575060015b611acd5750600092915050565b6000611ae4611adf83600001516114f6565b611d44565b50905060008260a001516001811115611aff57611aff613cd6565b03611b1657611b0e8282613274565b949350505050565b60018260a001516001811115611b2e57611b2e613cd6565b03611b3d57611b0e828261329e565b5060009392505050565b600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303611c23577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663b810c6366040518163ffffffff1660e01b81526004016040805180830381865afa158015611bf0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1491906142a3565b509050611c20816132c8565b90565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663062af0b06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb29190613fab565b604051909150600090339083908381818185875af1925050503d8060008114611cf7576040519150601f19603f3d011682016040523d82523d6000602084013e611cfc565b606091505b5050905080611d40576040517fcfbb474c0000000000000000000000000000000000000000000000000000000081526004810183905233602482015260440161099a565b5090565b60008060008373ffffffffffffffffffffffffffffffffffffffff1663cdf456e16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611db89190613fab565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16633b798e8a846040518263ffffffff1660e01b8152600401611e1891815260200190565b6040805180830381865afa158015611e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e5891906142d3565b90925090506000611e6a607842613ff3565b821015611f23576001905060008773ffffffffffffffffffffffffffffffffffffffff1663d24378eb6040518163ffffffff1660e01b81526004016040805180830381865afa158015611ec1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee591906142f7565b90945090508015611f21576040517ebfc92100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b9196919550909350505050565b60008215801590611f4057508115155b611f4c57611f4c61415b565b5060009118121590565b6000811315611f6857611f68816133b6565b6040517f88a3c8480000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8316906388a3c848906024015b600060405180830381600087803b158015611fd157600080fd5b505af1158015611fe5573d6000803e3d6000fd5b505050505050565b6040517f85f05ab500000000000000000000000000000000000000000000000000000000815260048101839052602481018290527f4b57454e54410000000000000000000000000000000000000000000000000000604482015273ffffffffffffffffffffffffffffffffffffffff8416906385f05ab5906064015b600060405180830381600087803b15801561208357600080fd5b505af1158015612097573d6000803e3d6000fd5b50505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146120f1576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b60008113156123065773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166323b872dd333060ff85901c600003808601186040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff938416600482015292909116602483015260448201526064016020604051808303816000875af115801561222d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122519190613edb565b5073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166328ba84ca3360ff84901c600003808501186040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401600060405180830381600087803b1580156114db57600080fd5b600081121561247957612318816133b6565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663a9059cbb3360ff84901c600003808501186040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303816000875af11580156123d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f59190613edb565b506040517fb014da2100000000000000000000000000000000000000000000000000000000815233600482015260ff82901c6000038083011860248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b014da21906044016114c1565b50565b801561247957604051600090339083908381818185875af1925050503d80600081146124c4576040519150601f19603f3d011682016040523d82523d6000602084013e6124c9565b606091505b5050905080612504576040517f0ca79afd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f79a49527000000000000000000000000000000000000000000000000000000008152336004820152602481018390527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906379a4952790604401611fb7565b36600080600061258e87878761341f565b9890975095505050505050565b6000806125a88484613482565b9150915060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161480156126b457506040517fb5af090f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063b5af090f90602401602060405180830381865afa158015612690573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b49190613edb565b15612785576126c2876133b6565b506040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301526024820188905233919084169063a9059cbb906044016020604051808303816000875af115801561275b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061277f9190613edb565b506129e6565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614801561288b57506040517fb5af090f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063b5af090f90602401602060405180830381865afa158015612867573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061288b9190613edb565b15612994577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166336c78516337f00000000000000000000000000000000000000000000000000000000000000006128f78b6134db565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529183166024830152821660448201529086166064820152608401600060405180830381600087803b15801561297457600080fd5b505af1158015612988573d6000803e3d6000fd5b505050503090506129e6565b6040517fae943ea900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80851660048301528316602482015260440161099a565b6129f3818888888861352b565b6040517f3b9d50e700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301528381166024830152828116604483015260648201899052608482018890527f00000000000000000000000000000000000000000000000000000000000000001690633b9d50e79060a401600060405180830381600087803b158015612a9a57600080fd5b505af1158015612aae573d6000803e3d6000fd5b5050505050505050505050565b8073ffffffffffffffffffffffffffffffffffffffff16635a1cbd2b6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156114db57600080fd5b6040517f32f0510300000000000000000000000000000000000000000000000000000000815260048101839052602481018290527f4b57454e54410000000000000000000000000000000000000000000000000000604482015273ffffffffffffffffffffffffffffffffffffffff8416906332f0510390606401612069565b6040517f787d6c300000000000000000000000000000000000000000000000000000000081526004810184905260248101839052604481018290527f4b57454e54410000000000000000000000000000000000000000000000000000606482015273ffffffffffffffffffffffffffffffffffffffff85169063787d6c3090608401600060405180830381600087803b158015612c1f57600080fd5b505af1158015612c33573d6000803e3d6000fd5b5050505050505050565b6040517f5c8011c3000000000000000000000000000000000000000000000000000000008152600481018290527f4b57454e54410000000000000000000000000000000000000000000000000000602482015273ffffffffffffffffffffffffffffffffffffffff831690635c8011c390604401611fb7565b6040517fc5a4b07a00000000000000000000000000000000000000000000000000000000815260048101839052602481018290527f4b57454e54410000000000000000000000000000000000000000000000000000604482015273ffffffffffffffffffffffffffffffffffffffff84169063c5a4b07a90606401612069565b6040517fed44a2db000000000000000000000000000000000000000000000000000000008152600481018290527f4b57454e54410000000000000000000000000000000000000000000000000000602482015273ffffffffffffffffffffffffffffffffffffffff83169063ed44a2db90604401611fb7565b6040517fc70b41e900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff82169063c70b41e9906024016114c1565b6040517fdcce580600000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff82169063dcce5806906024016114c1565b84600003612e8d576040517f0287a76700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000861315612ec257612e9f866133b6565b60ff86901c6000038087011860156000828254612ebc9190614323565b90915550505b6000612ecc613682565b9050604051806101000160405280898152602001888152602001878152602001868152602001828152602001856001811115612f0a57612f0a613cd6565b8152602001848152602001831515815250601760006016548152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a08201518160050160006101000a81548160ff02191690836001811115612f8c57612f8c613cd6565b021790555060c0820151600682015560e090910151600790910180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790556016546040517f146c64fc0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169163146c64fc91613057919085908d908d908d908d908d908d908d90600401614336565b600060405180830381600087803b15801561307157600080fd5b505af1158015613085573d6000803e3d6000fd5b505050506016600081546130989061438c565b909155505050505050505050565b60006130b182611116565b90506000816020015113156130e857602081015160ff81901c60000390810118601560008282546130e29190613ff3565b90915550505b60808101516040517fee8ca3b500000000000000000000000000000000000000000000000000000000815260048101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063ee8ca3b590602401600060405180830381600087803b15801561317657600080fd5b505af115801561318a573d6000803e3d6000fd5b50505060008381526017602052604080822082815560018101839055600281018390556003810183905560048082018490556005820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009081169091556006830185905560079092018054909216909155608085015191517fd63f1ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016945063d63f1ca693611fb79388939092016140ab565b6000808360400151131561329057506060820151811115610635565b506060820151811015610635565b600080836040015113156132ba57506060820151811015610635565b506060820151811115610635565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114613342576040519150601f19603f3d011682016040523d82523d6000602084013e613347565b606091505b50509050806133b2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f707352656164793a20455448207472616e73666572206661696c6564000000604482015260640161099a565b5050565b6133be6107f1565b60ff82901c600003808301181115612479576133d86107f1565b60ff82901c600003808301186040517f3cb0273a0000000000000000000000000000000000000000000000000000000081526004810192909252602482015260440161099a565b600581901b8301358301803590602080820191869003016134408184614323565b851015613479576040517f3b99b53d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50935093915050565b60008061348f84846137f2565b91505b600061349e8585613805565b905080156134b9576134b0858561383e565b945094506134ce565b6134c38585613865565b93506134d492505050565b50613492565b9250929050565b600073ffffffffffffffffffffffffffffffffffffffff821115611d40576040517fc4bd89a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001808252818301909252600091816020015b606081526020019060019003908161354257905050905085858585856000604051602001613575969594939291906143c4565b6040516020818303038152906040528160008151811061359757613597613ef6565b60200260200101819052507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166324856bc3600060f81b60405160200161361b91907fff0000000000000000000000000000000000000000000000000000000000000091909116815260010190565b604051602081830303815290604052836040518363ffffffff1660e01b815260040161364892919061448b565b600060405180830381600087803b15801561366257600080fd5b505af1158015613676573d6000803e3d6000fd5b50505050505050505050565b60008061368d6138da565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16633323b467303073ffffffffffffffffffffffffffffffffffffffff166385652fec6016546040516024016136fd91815260200190565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291815260208201805160e094851b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116179052519185901b7fffffffff000000000000000000000000000000000000000000000000000000001682526137a993925090869073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee906004016144b9565b6020604051808303816000875af11580156137c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137ec9190613fab565b91505090565b60006137fe8383613a36565b9392505050565b600061381360036014614323565b6014613820600382614323565b61382a9190614323565b6138349190614323565b9091101592915050565b366000838361384f60036014614323565b61385a92829061457f565b915091509250929050565b600080806014613876600382614323565b6138809190614323565b8410156138b9576040517f3b99b53d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050508135606081811c9460489290921c62ffffff169360170135901c9150565b6040805180820182526060808252602082015281516001818401818152608083019094529192909182919081602001602082028036833701905050815260408051600180825281830190925260209092019190816020015b6060815260200190600190039081613932575050905280518051919250600091829061396057613960613ef6565b6020026020010190600381111561397957613979613cd6565b9081600381111561398c5761398c613cd6565b905250601654604051309182916394e05b2c916139af9160240190815260200190565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051602001613a029291906145a9565b6040516020818303038152906040528160200151600081518110613a2857613a28613ef6565b602002602001018190525090565b60006014821015613a73576040517f3b99b53d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50503560601c90565b600060208284031215613a8e57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461247957600080fd5b600060208284031215613ac957600080fd5b81356137fe81613a95565b60008083601f840112613ae657600080fd5b50813567ffffffffffffffff811115613afe57600080fd5b6020830191508360208260051b85010111156134d457600080fd5b60008060008060408587031215613b2f57600080fd5b843567ffffffffffffffff80821115613b4757600080fd5b613b5388838901613ad4565b90965094506020870135915080821115613b6c57600080fd5b50613b7987828801613ad4565b95989497509550505050565b6000610120820190508251151582526020830151600f0b60208301526040830151613bc460408401826fffffffffffffffffffffffffffffffff169052565b506060830151613be860608401826fffffffffffffffffffffffffffffffff169052565b506080830151613c0c60808401826fffffffffffffffffffffffffffffffff169052565b5060a0830151613c3060a08401826fffffffffffffffffffffffffffffffff169052565b5060c083015160c083015260e083015160e083015261010080840151818401525092915050565b6000815180845260005b81811015613c7d57602081850181015186830182015201613c61565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b8215158152604060208201526000611b0e6040830184613c57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061247957612479613cd6565b600061010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a0830151613d5881613d05565b8060a08401525060c083015160c083015260e0830151151560e083015292915050565b604051610120810167ffffffffffffffff81118282101715613dc6577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405290565b805167ffffffffffffffff811681146115cd57600080fd5b80516fffffffffffffffffffffffffffffffff811681146115cd57600080fd5b8051600f81900b81146115cd57600080fd5b600060a08284031215613e2857600080fd5b60405160a0810181811067ffffffffffffffff82111715613e72577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052613e7e83613dcc565b8152613e8c60208401613dcc565b6020820152613e9d60408401613de4565b6040820152613eae60608401613de4565b6060820152613ebf60808401613e04565b60808201529392505050565b805180151581146115cd57600080fd5b600060208284031215613eed57600080fd5b6137fe82613ecb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215613f3757600080fd5b8135601081106137fe57600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613f7b57600080fd5b83018035915067ffffffffffffffff821115613f9657600080fd5b6020019150368190038213156134d457600080fd5b600060208284031215613fbd57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561063557610635613fc4565b6000610120828403121561401957600080fd5b614021613d7b565b61402a83613ecb565b815261403860208401613e04565b602082015261404960408401613de4565b604082015261405a60608401613de4565b606082015261406b60808401613de4565b608082015261407c60a08401613de4565b60a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b83815260208101839052606081016140c283613d05565b826040830152949350505050565b60007f8000000000000000000000000000000000000000000000000000000000000000820361410157614101613fc4565b5060000390565b600060a08201905086825285602083015284604083015283606083015261412e83613d05565b8260808301529695505050505050565b60006020828403121561415057600080fd5b81516137fe81613a95565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b803565ffffffffffff811681146115cd57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600061010073ffffffffffffffffffffffffffffffffffffffff8088168452863561421381613a95565b818116602086015250602087013561422a81613a95565b81811660408601525061423f6040880161418a565b65ffffffffffff80821660608701528061425b60608b0161418a565b1660808701525050608087013561427181613a95565b81811660a0860152505060a086013560c08401528060e084015261429881840185876141a0565b979650505050505050565b600080604083850312156142b657600080fd5b8251915060208301516142c881613a95565b809150509250929050565b600080604083850312156142e657600080fd5b505080516020909101519092909150565b6000806040838503121561430a57600080fd5b8251915061431a60208401613ecb565b90509250929050565b8082018082111561063557610635613fc4565b6000610120820190508a82528960208301528860408301528760608301528660808301528560a083015261436985613d05565b8460c08301528360e08301528215156101008301529a9950505050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036143bd576143bd613fc4565b5060010190565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015284604082015260a06060820152600061440060a0830185876141a0565b90508215156080830152979650505050505050565b600082825180855260208086019550808260051b84010181860160005b8481101561447e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301895261446c838351613c57565b98840198925090830190600101614432565b5090979650505050505050565b60408152600061449e6040830185613c57565b82810360208401526144b08185614415565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff85168152600060206080818401526144e96080840187613c57565b8381036040808601919091528651818352805191830182905283019060009060608401905b8083101561453e5783516004811061452857614528613cd6565b825292850192600192909201919085019061450e565b50848901519250838103858501526145568184614415565b955050505050506144b0606083018473ffffffffffffffffffffffffffffffffffffffff169052565b6000808585111561458f57600080fd5b8386111561459c57600080fd5b5050820193919092039150565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000611b0e6040830184613c5756fea2646970667358221220012c0a889520577fb5eb290896b7f0035ebbb680fd3478b66945084ac347506a64736f6c634300081200330000000000000000000000008234f990b149ae59416dc260305e565e5dafeb54000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b0000000000000000000000008c6f28f2f1a3c87f0f938b96d27520d9751ec8d90000000000000000000000002c15259d4886e2c0946f9ab7a5e389c86b3c3b04000000000000000000000000d30bdfd7e7a65fe109d5de1d4e95f3b800fb7463000000000000000000000000e8c41be1a167314abaf2423b72bf8da826943ffd00000000000000000000000001051113d81d7d6da508462f2ad6d7fd96cf42ef000000000000000000000000340759c8346a1e6ed92035fb8b6ec57ce1d82c2c000000000000000000000000865da103d126b3be3599d84cab57109a861f56310000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3

Deployed Bytecode

0x6080604052600436106101795760003560e01c806385652fec116100cb578063cdf8972e1161007f578063eff557a711610059578063eff557a7146104e9578063f2fde38b1461051d578063ffa1ad741461053d57600080fd5b8063cdf8972e1461047c578063d6c7ef8b146104a9578063e71bdf41146104c957600080fd5b80638f32d59b116100b05780638f32d59b1461040b57806394e05b2c14610438578063a48903371461046657600080fd5b806385652fec146103be5780638da5cb5b146103de57600080fd5b8063613838051161012d57806372a69c0d1161010757806372a69c0d1461035457806378e91014146103695780638322fff21461039657600080fd5b806361383805146102eb57806367e7646f146103005780636afdd8501461032057600080fd5b80634c2eefb41161015e5780634c2eefb414610272578063587cde1e14610296578063608d60eb146102d657600080fd5b80631928b3cb14610185578063472d04471461021957600080fd5b3661018057005b600080fd5b34801561019157600080fd5b506101a56101a0366004613a7c565b610571565b6040516102109190600060a08201905067ffffffffffffffff8084511683528060208501511660208401525060408301516fffffffffffffffffffffffffffffffff808216604085015280606086015116606085015250506080830151600f0b608083015292915050565b60405180910390f35b34801561022557600080fd5b5061024d7f000000000000000000000000340759c8346a1e6ed92035fb8b6ec57ce1d82c2c81565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610210565b34801561027e57600080fd5b5061028860165481565b604051908152602001610210565b3480156102a257600080fd5b506102c66102b1366004613ab7565b60016020526000908152604090205460ff1681565b6040519015158152602001610210565b6102e96102e4366004613b19565b61063b565b005b3480156102f757600080fd5b506102886107f1565b34801561030c57600080fd5b506102e961031b366004613ab7565b6108b5565b34801561032c57600080fd5b5061024d7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba381565b34801561036057600080fd5b506102c6610a18565b34801561037557600080fd5b50610389610384366004613a7c565b610a51565b6040516102109190613b85565b3480156103a257600080fd5b5061024d73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b3480156103ca57600080fd5b506102e96103d9366004613a7c565b610b34565b3480156103ea57600080fd5b5060005461024d9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561041757600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff1633146102c6565b34801561044457600080fd5b50610458610453366004613a7c565b61108f565b604051610210929190613cbb565b34801561047257600080fd5b5061028860155481565b34801561048857600080fd5b5061049c610497366004613a7c565b611116565b6040516102109190613d15565b3480156104b557600080fd5b506102e96104c4366004613ab7565b611200565b3480156104d557600080fd5b506102e96104e4366004613ab7565b6112df565b3480156104f557600080fd5b5061024d7f00000000000000000000000001051113d81d7d6da508462f2ad6d7fd96cf42ef81565b34801561052957600080fd5b506102e9610538366004613ab7565b611442565b34801561054957600080fd5b506102887f322e312e3000000000000000000000000000000000000000000000000000000081565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526105a5826114f6565b6040517f55f5751000000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906355f575109060240160a060405180830381865afa158015610611573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106359190613e16565b92915050565b601854600203610677576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60026018819055507f000000000000000000000000865da103d126b3be3599d84cab57109a861f563173ffffffffffffffffffffffffffffffffffffffff1663125127b36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070e9190613edb565b610744576040517f274ccc0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8281811461077e576040517fff633a3800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156107e4576107dc86868381811061079e5761079e613ef6565b90506020020160208101906107b39190613f25565b8585848181106107c5576107c5613ef6565b90506020028101906107d79190613f46565b6115d2565b600101610781565b5050600160185550505050565b6015546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000919073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008c6f28f2f1a3c87f0f938b96d27520d9751ec8d916906370a0823190602401602060405180830381865afa158015610882573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a69190613fab565b6108b09190613ff3565b905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314610906576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116158061094f575073ffffffffffffffffffffffffffffffffffffffff811660009081526001602052604090205460ff16155b156109a3576040517fa3653d9f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811660008181526001602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555133917faab1e52e5e9244a59f6d721ebad6da39111d47dba7c5b9cd735a06f839e8eb0291a350565b6000805473ffffffffffffffffffffffffffffffffffffffff163314806108b05750503360009081526001602052604090205460ff1690565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810191909152610aa3826114f6565b6040517fc8b809aa00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff919091169063c8b809aa9060240161012060405180830381865afa158015610b10573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106359190614006565b601854600203610b70576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60026018819055507f000000000000000000000000865da103d126b3be3599d84cab57109a861f563173ffffffffffffffffffffffffffffffffffffffff1663125127b36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610be3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c079190613edb565b610c3d576040517f274ccc0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c4681611a08565b610c85576040517f4316b3d50000000000000000000000000000000000000000000000000000000081526004810182905233602482015260440161099a565b6000610c9082611116565b60008381526017602052604080822082815560018101839055600281018390556003810183905560048082018490556005820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116909155600683019490945560079091018054909316909255608083015190517fee8ca3b5000000000000000000000000000000000000000000000000000000008152918201529091507f000000000000000000000000340759c8346a1e6ed92035fb8b6ec57ce1d82c2c73ffffffffffffffffffffffffffffffffffffffff169063ee8ca3b590602401600060405180830381600087803b158015610d8d57600080fd5b505af1158015610da1573d6000803e3d6000fd5b505050506000610daf611b47565b90506000610dc083600001516114f6565b9050600080610dce83611d44565b915091508460e0015115610f7c576040517f55f5751000000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8516906355f575109060240160a060405180830381865afa158015610e49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6d9190613e16565b60800151600f0b9050801580610e8c5750610e8c818760400151611f30565b15610f485760808601516040517fd63f1ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b169163d63f1ca691610f0b918b916001906004016140ab565b600060405180830381600087803b158015610f2557600080fd5b505af1158015610f39573d6000803e3d6000fd5b50505050505050505050611087565b60ff81901c60000380820118604087015160ff81901c600003908101181115610f7a57610f74816140d0565b60408701525b505b600085602001511315610fb157602085015160ff81901c6000039081011860156000828254610fab9190613ff3565b90915550505b610fbf838660200151611f56565b610fd28386604001518760c00151611fed565b60808501516040517f568824dc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b169163568824dc9161104f918a9187908a908890600401614108565b600060405180830381600087803b15801561106957600080fd5b505af115801561107d573d6000803e3d6000fd5b5050505050505050505b506001601855565b6000606061109c83611a08565b91503073ffffffffffffffffffffffffffffffffffffffff166385652fec846040516024016110cd91815260200190565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050915091565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810191909152601760008381526020019081526020016000206040518061010001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820160009054906101000a900460ff1660018111156111cc576111cc613cd6565b60018111156111dd576111dd613cd6565b81526006820154602082015260079091015460ff16151560409091015292915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008234f990b149ae59416dc260305e565e5dafeb54161461126f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b60005473ffffffffffffffffffffffffffffffffffffffff163314611330576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81161580611378575073ffffffffffffffffffffffffffffffffffffffff811660009081526001602052604090205460ff165b156113c7576040517fa3653d9f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240161099a565b73ffffffffffffffffffffffffffffffffffffffff8116600081815260016020819052604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169092179091555133917f52d2da59f56f31503d90a3f1f08001c5705116f9e44f9ee08a7ebe1354b2cf4891a350565b61144b816120a0565b6040517f2e9ca20400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301523360248301527f0000000000000000000000008234f990b149ae59416dc260305e565e5dafeb541690632e9ca204906044015b600060405180830381600087803b1580156114db57600080fd5b505af11580156114ef573d6000803e3d6000fd5b5050505050565b6040517fe63bfadb000000000000000000000000000000000000000000000000000000008152600481018290526000907f000000000000000000000000d30bdfd7e7a65fe109d5de1d4e95f3b800fb746373ffffffffffffffffffffffffffffffffffffffff169063e63bfadb90602401602060405180830381865afa158015611584573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a8919061413e565b905073ffffffffffffffffffffffffffffffffffffffff81166115cd576115cd61415b565b919050565b600083600f8111156115e6576115e6613cd6565b905060028110806115f7575080600e145b80611602575080600f145b156117bb5760005473ffffffffffffffffffffffffffffffffffffffff163314611658576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084600f81111561166c5761166c613cd6565b0361168257823561167c81612161565b50611a02565b600184600f81111561169657611696613cd6565b036116a657823561167c8161247c565b600e84600f8111156116ba576116ba613cd6565b036116f45760008036816116d08787600261257d565b9150915086359350602087013592506116eb8484848461259b565b50505050611a02565b823660006117048386600661257d565b6040517f2b67b570000000000000000000000000000000000000000000000000000000008152919350915073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31690632b67b570906117819033908790879087906004016141e9565b600060405180830381600087803b15801561179b57600080fd5b505af11580156117af573d6000803e3d6000fd5b50505050505050611a02565b6117c3610a18565b6117f9576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481101561183e57600284600f81111561181657611816613cd6565b03611833578235602084013561182c8282611f56565b5050611a02565b823561167c81612abb565b600681101561189c57600484600f81111561185b5761185b613cd6565b0361187f57823560208401356040850135611877838383612b03565b505050611a02565b82356020840135604085013560608601356116eb84848484612b83565b60088110156118e657600684600f8111156118b9576118b9613cd6565b036118d557823560208401356040850135611877838383611fed565b8235602084013561182c8282612c3d565b600a81101561193057600884600f81111561190357611903613cd6565b0361191f57823560208401356040850135611877838383612cb6565b8235602084013561182c8282612d36565b600c81101561196857600a84600f81111561194d5761194d613cd6565b0361195d57823561167c81612daf565b823561167c81612e01565b600e8110156119c457600c84600f81111561198557611985613cd6565b036119b9578235602084013560408501356060860135608087013560a088013560c08901356117af87878787878787612e53565b823561167c816130a6565b600f811115611a02576040517fd76a1e9e0000000000000000000000000000000000000000000000000000000081526004810182905260240161099a565b50505050565b600080611a1483611116565b8051909150611a265750600092915050565b80516040517f856aae6c00000000000000000000000000000000000000000000000000000000815260048101919091527f000000000000000000000000e8c41be1a167314abaf2423b72bf8da826943ffd73ffffffffffffffffffffffffffffffffffffffff169063856aae6c9060240160006040518083038186803b158015611aaf57600080fd5b505afa925050508015611ac0575060015b611acd5750600092915050565b6000611ae4611adf83600001516114f6565b611d44565b50905060008260a001516001811115611aff57611aff613cd6565b03611b1657611b0e8282613274565b949350505050565b60018260a001516001811115611b2e57611b2e613cd6565b03611b3d57611b0e828261329e565b5060009392505050565b600073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000340759c8346a1e6ed92035fb8b6ec57ce1d82c2c163303611c23577f000000000000000000000000340759c8346a1e6ed92035fb8b6ec57ce1d82c2c73ffffffffffffffffffffffffffffffffffffffff1663b810c6366040518163ffffffff1660e01b81526004016040805180830381865afa158015611bf0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1491906142a3565b509050611c20816132c8565b90565b7f000000000000000000000000865da103d126b3be3599d84cab57109a861f563173ffffffffffffffffffffffffffffffffffffffff1663062af0b06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb29190613fab565b604051909150600090339083908381818185875af1925050503d8060008114611cf7576040519150601f19603f3d011682016040523d82523d6000602084013e611cfc565b606091505b5050905080611d40576040517fcfbb474c0000000000000000000000000000000000000000000000000000000081526004810183905233602482015260440161099a565b5090565b60008060008373ffffffffffffffffffffffffffffffffffffffff1663cdf456e16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611db89190613fab565b90506000807f0000000000000000000000002c15259d4886e2c0946f9ab7a5e389c86b3c3b0473ffffffffffffffffffffffffffffffffffffffff16633b798e8a846040518263ffffffff1660e01b8152600401611e1891815260200190565b6040805180830381865afa158015611e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e5891906142d3565b90925090506000611e6a607842613ff3565b821015611f23576001905060008773ffffffffffffffffffffffffffffffffffffffff1663d24378eb6040518163ffffffff1660e01b81526004016040805180830381865afa158015611ec1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee591906142f7565b90945090508015611f21576040517ebfc92100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b9196919550909350505050565b60008215801590611f4057508115155b611f4c57611f4c61415b565b5060009118121590565b6000811315611f6857611f68816133b6565b6040517f88a3c8480000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8316906388a3c848906024015b600060405180830381600087803b158015611fd157600080fd5b505af1158015611fe5573d6000803e3d6000fd5b505050505050565b6040517f85f05ab500000000000000000000000000000000000000000000000000000000815260048101839052602481018290527f4b57454e54410000000000000000000000000000000000000000000000000000604482015273ffffffffffffffffffffffffffffffffffffffff8416906385f05ab5906064015b600060405180830381600087803b15801561208357600080fd5b505af1158015612097573d6000803e3d6000fd5b50505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146120f1576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b60008113156123065773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008c6f28f2f1a3c87f0f938b96d27520d9751ec8d9166323b872dd333060ff85901c600003808601186040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff938416600482015292909116602483015260448201526064016020604051808303816000875af115801561222d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122519190613edb565b5073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b166328ba84ca3360ff84901c600003808501186040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401600060405180830381600087803b1580156114db57600080fd5b600081121561247957612318816133b6565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008c6f28f2f1a3c87f0f938b96d27520d9751ec8d91663a9059cbb3360ff84901c600003808501186040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303816000875af11580156123d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f59190613edb565b506040517fb014da2100000000000000000000000000000000000000000000000000000000815233600482015260ff82901c6000038083011860248201527f000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b73ffffffffffffffffffffffffffffffffffffffff169063b014da21906044016114c1565b50565b801561247957604051600090339083908381818185875af1925050503d80600081146124c4576040519150601f19603f3d011682016040523d82523d6000602084013e6124c9565b606091505b5050905080612504576040517f0ca79afd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f79a49527000000000000000000000000000000000000000000000000000000008152336004820152602481018390527f000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b73ffffffffffffffffffffffffffffffffffffffff16906379a4952790604401611fb7565b36600080600061258e87878761341f565b9890975095505050505050565b6000806125a88484613482565b9150915060007f0000000000000000000000008c6f28f2f1a3c87f0f938b96d27520d9751ec8d973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161480156126b457506040517fb5af090f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301527f000000000000000000000000865da103d126b3be3599d84cab57109a861f5631169063b5af090f90602401602060405180830381865afa158015612690573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b49190613edb565b15612785576126c2876133b6565b506040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad811660048301526024820188905233919084169063a9059cbb906044016020604051808303816000875af115801561275b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061277f9190613edb565b506129e6565b7f0000000000000000000000008c6f28f2f1a3c87f0f938b96d27520d9751ec8d973ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614801561288b57506040517fb5af090f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301527f000000000000000000000000865da103d126b3be3599d84cab57109a861f5631169063b5af090f90602401602060405180830381865afa158015612867573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061288b9190613edb565b15612994577f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba373ffffffffffffffffffffffffffffffffffffffff166336c78516337f0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad6128f78b6134db565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529183166024830152821660448201529086166064820152608401600060405180830381600087803b15801561297457600080fd5b505af1158015612988573d6000803e3d6000fd5b505050503090506129e6565b6040517fae943ea900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80851660048301528316602482015260440161099a565b6129f3818888888861352b565b6040517f3b9d50e700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301528381166024830152828116604483015260648201899052608482018890527f000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b1690633b9d50e79060a401600060405180830381600087803b158015612a9a57600080fd5b505af1158015612aae573d6000803e3d6000fd5b5050505050505050505050565b8073ffffffffffffffffffffffffffffffffffffffff16635a1cbd2b6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156114db57600080fd5b6040517f32f0510300000000000000000000000000000000000000000000000000000000815260048101839052602481018290527f4b57454e54410000000000000000000000000000000000000000000000000000604482015273ffffffffffffffffffffffffffffffffffffffff8416906332f0510390606401612069565b6040517f787d6c300000000000000000000000000000000000000000000000000000000081526004810184905260248101839052604481018290527f4b57454e54410000000000000000000000000000000000000000000000000000606482015273ffffffffffffffffffffffffffffffffffffffff85169063787d6c3090608401600060405180830381600087803b158015612c1f57600080fd5b505af1158015612c33573d6000803e3d6000fd5b5050505050505050565b6040517f5c8011c3000000000000000000000000000000000000000000000000000000008152600481018290527f4b57454e54410000000000000000000000000000000000000000000000000000602482015273ffffffffffffffffffffffffffffffffffffffff831690635c8011c390604401611fb7565b6040517fc5a4b07a00000000000000000000000000000000000000000000000000000000815260048101839052602481018290527f4b57454e54410000000000000000000000000000000000000000000000000000604482015273ffffffffffffffffffffffffffffffffffffffff84169063c5a4b07a90606401612069565b6040517fed44a2db000000000000000000000000000000000000000000000000000000008152600481018290527f4b57454e54410000000000000000000000000000000000000000000000000000602482015273ffffffffffffffffffffffffffffffffffffffff83169063ed44a2db90604401611fb7565b6040517fc70b41e900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff82169063c70b41e9906024016114c1565b6040517fdcce580600000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff82169063dcce5806906024016114c1565b84600003612e8d576040517f0287a76700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000861315612ec257612e9f866133b6565b60ff86901c6000038087011860156000828254612ebc9190614323565b90915550505b6000612ecc613682565b9050604051806101000160405280898152602001888152602001878152602001868152602001828152602001856001811115612f0a57612f0a613cd6565b8152602001848152602001831515815250601760006016548152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a08201518160050160006101000a81548160ff02191690836001811115612f8c57612f8c613cd6565b021790555060c0820151600682015560e090910151600790910180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790556016546040517f146c64fc0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b73ffffffffffffffffffffffffffffffffffffffff169163146c64fc91613057919085908d908d908d908d908d908d908d90600401614336565b600060405180830381600087803b15801561307157600080fd5b505af1158015613085573d6000803e3d6000fd5b505050506016600081546130989061438c565b909155505050505050505050565b60006130b182611116565b90506000816020015113156130e857602081015160ff81901c60000390810118601560008282546130e29190613ff3565b90915550505b60808101516040517fee8ca3b500000000000000000000000000000000000000000000000000000000815260048101919091527f000000000000000000000000340759c8346a1e6ed92035fb8b6ec57ce1d82c2c73ffffffffffffffffffffffffffffffffffffffff169063ee8ca3b590602401600060405180830381600087803b15801561317657600080fd5b505af115801561318a573d6000803e3d6000fd5b50505060008381526017602052604080822082815560018101839055600281018390556003810183905560048082018490556005820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009081169091556006830185905560079092018054909216909155608085015191517fd63f1ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b16945063d63f1ca693611fb79388939092016140ab565b6000808360400151131561329057506060820151811115610635565b506060820151811015610635565b600080836040015113156132ba57506060820151811015610635565b506060820151811115610635565b60007f00000000000000000000000001051113d81d7d6da508462f2ad6d7fd96cf42ef73ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114613342576040519150601f19603f3d011682016040523d82523d6000602084013e613347565b606091505b50509050806133b2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f707352656164793a20455448207472616e73666572206661696c6564000000604482015260640161099a565b5050565b6133be6107f1565b60ff82901c600003808301181115612479576133d86107f1565b60ff82901c600003808301186040517f3cb0273a0000000000000000000000000000000000000000000000000000000081526004810192909252602482015260440161099a565b600581901b8301358301803590602080820191869003016134408184614323565b851015613479576040517f3b99b53d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50935093915050565b60008061348f84846137f2565b91505b600061349e8585613805565b905080156134b9576134b0858561383e565b945094506134ce565b6134c38585613865565b93506134d492505050565b50613492565b9250929050565b600073ffffffffffffffffffffffffffffffffffffffff821115611d40576040517fc4bd89a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001808252818301909252600091816020015b606081526020019060019003908161354257905050905085858585856000604051602001613575969594939291906143c4565b6040516020818303038152906040528160008151811061359757613597613ef6565b60200260200101819052507f0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad73ffffffffffffffffffffffffffffffffffffffff166324856bc3600060f81b60405160200161361b91907fff0000000000000000000000000000000000000000000000000000000000000091909116815260010190565b604051602081830303815290604052836040518363ffffffff1660e01b815260040161364892919061448b565b600060405180830381600087803b15801561366257600080fd5b505af1158015613676573d6000803e3d6000fd5b50505050505050505050565b60008061368d6138da565b90507f000000000000000000000000340759c8346a1e6ed92035fb8b6ec57ce1d82c2c73ffffffffffffffffffffffffffffffffffffffff16633323b467303073ffffffffffffffffffffffffffffffffffffffff166385652fec6016546040516024016136fd91815260200190565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291815260208201805160e094851b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116179052519185901b7fffffffff000000000000000000000000000000000000000000000000000000001682526137a993925090869073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee906004016144b9565b6020604051808303816000875af11580156137c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137ec9190613fab565b91505090565b60006137fe8383613a36565b9392505050565b600061381360036014614323565b6014613820600382614323565b61382a9190614323565b6138349190614323565b9091101592915050565b366000838361384f60036014614323565b61385a92829061457f565b915091509250929050565b600080806014613876600382614323565b6138809190614323565b8410156138b9576040517f3b99b53d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050508135606081811c9460489290921c62ffffff169360170135901c9150565b6040805180820182526060808252602082015281516001818401818152608083019094529192909182919081602001602082028036833701905050815260408051600180825281830190925260209092019190816020015b6060815260200190600190039081613932575050905280518051919250600091829061396057613960613ef6565b6020026020010190600381111561397957613979613cd6565b9081600381111561398c5761398c613cd6565b905250601654604051309182916394e05b2c916139af9160240190815260200190565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051602001613a029291906145a9565b6040516020818303038152906040528160200151600081518110613a2857613a28613ef6565b602002602001018190525090565b60006014821015613a73576040517f3b99b53d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50503560601c90565b600060208284031215613a8e57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461247957600080fd5b600060208284031215613ac957600080fd5b81356137fe81613a95565b60008083601f840112613ae657600080fd5b50813567ffffffffffffffff811115613afe57600080fd5b6020830191508360208260051b85010111156134d457600080fd5b60008060008060408587031215613b2f57600080fd5b843567ffffffffffffffff80821115613b4757600080fd5b613b5388838901613ad4565b90965094506020870135915080821115613b6c57600080fd5b50613b7987828801613ad4565b95989497509550505050565b6000610120820190508251151582526020830151600f0b60208301526040830151613bc460408401826fffffffffffffffffffffffffffffffff169052565b506060830151613be860608401826fffffffffffffffffffffffffffffffff169052565b506080830151613c0c60808401826fffffffffffffffffffffffffffffffff169052565b5060a0830151613c3060a08401826fffffffffffffffffffffffffffffffff169052565b5060c083015160c083015260e083015160e083015261010080840151818401525092915050565b6000815180845260005b81811015613c7d57602081850181015186830182015201613c61565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b8215158152604060208201526000611b0e6040830184613c57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061247957612479613cd6565b600061010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a0830151613d5881613d05565b8060a08401525060c083015160c083015260e0830151151560e083015292915050565b604051610120810167ffffffffffffffff81118282101715613dc6577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405290565b805167ffffffffffffffff811681146115cd57600080fd5b80516fffffffffffffffffffffffffffffffff811681146115cd57600080fd5b8051600f81900b81146115cd57600080fd5b600060a08284031215613e2857600080fd5b60405160a0810181811067ffffffffffffffff82111715613e72577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052613e7e83613dcc565b8152613e8c60208401613dcc565b6020820152613e9d60408401613de4565b6040820152613eae60608401613de4565b6060820152613ebf60808401613e04565b60808201529392505050565b805180151581146115cd57600080fd5b600060208284031215613eed57600080fd5b6137fe82613ecb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215613f3757600080fd5b8135601081106137fe57600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613f7b57600080fd5b83018035915067ffffffffffffffff821115613f9657600080fd5b6020019150368190038213156134d457600080fd5b600060208284031215613fbd57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561063557610635613fc4565b6000610120828403121561401957600080fd5b614021613d7b565b61402a83613ecb565b815261403860208401613e04565b602082015261404960408401613de4565b604082015261405a60608401613de4565b606082015261406b60808401613de4565b608082015261407c60a08401613de4565b60a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b83815260208101839052606081016140c283613d05565b826040830152949350505050565b60007f8000000000000000000000000000000000000000000000000000000000000000820361410157614101613fc4565b5060000390565b600060a08201905086825285602083015284604083015283606083015261412e83613d05565b8260808301529695505050505050565b60006020828403121561415057600080fd5b81516137fe81613a95565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b803565ffffffffffff811681146115cd57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600061010073ffffffffffffffffffffffffffffffffffffffff8088168452863561421381613a95565b818116602086015250602087013561422a81613a95565b81811660408601525061423f6040880161418a565b65ffffffffffff80821660608701528061425b60608b0161418a565b1660808701525050608087013561427181613a95565b81811660a0860152505060a086013560c08401528060e084015261429881840185876141a0565b979650505050505050565b600080604083850312156142b657600080fd5b8251915060208301516142c881613a95565b809150509250929050565b600080604083850312156142e657600080fd5b505080516020909101519092909150565b6000806040838503121561430a57600080fd5b8251915061431a60208401613ecb565b90509250929050565b8082018082111561063557610635613fc4565b6000610120820190508a82528960208301528860408301528760608301528660808301528560a083015261436985613d05565b8460c08301528360e08301528215156101008301529a9950505050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036143bd576143bd613fc4565b5060010190565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015284604082015260a06060820152600061440060a0830185876141a0565b90508215156080830152979650505050505050565b600082825180855260208086019550808260051b84010181860160005b8481101561447e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301895261446c838351613c57565b98840198925090830190600101614432565b5090979650505050505050565b60408152600061449e6040830185613c57565b82810360208401526144b08185614415565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff85168152600060206080818401526144e96080840187613c57565b8381036040808601919091528651818352805191830182905283019060009060608401905b8083101561453e5783516004811061452857614528613cd6565b825292850192600192909201919085019061450e565b50848901519250838103858501526145568184614415565b955050505050506144b0606083018473ffffffffffffffffffffffffffffffffffffffff169052565b6000808585111561458f57600080fd5b8386111561459c57600080fd5b5050820193919092039150565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000611b0e6040830184613c5756fea2646970667358221220012c0a889520577fb5eb290896b7f0035ebbb680fd3478b66945084ac347506a64736f6c63430008120033

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

0000000000000000000000008234f990b149ae59416dc260305e565e5dafeb54000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b0000000000000000000000008c6f28f2f1a3c87f0f938b96d27520d9751ec8d90000000000000000000000002c15259d4886e2c0946f9ab7a5e389c86b3c3b04000000000000000000000000d30bdfd7e7a65fe109d5de1d4e95f3b800fb7463000000000000000000000000e8c41be1a167314abaf2423b72bf8da826943ffd00000000000000000000000001051113d81d7d6da508462f2ad6d7fd96cf42ef000000000000000000000000340759c8346a1e6ed92035fb8b6ec57ce1d82c2c000000000000000000000000865da103d126b3be3599d84cab57109a861f56310000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3

-----Decoded View---------------
Arg [0] : _params (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 0000000000000000000000008234f990b149ae59416dc260305e565e5dafeb54
Arg [1] : 000000000000000000000000b753d2ee5dca1ff39a83ca3ec500656c31be940b
Arg [2] : 0000000000000000000000008c6f28f2f1a3c87f0f938b96d27520d9751ec8d9
Arg [3] : 0000000000000000000000002c15259d4886e2c0946f9ab7a5e389c86b3c3b04
Arg [4] : 000000000000000000000000d30bdfd7e7a65fe109d5de1d4e95f3b800fb7463
Arg [5] : 000000000000000000000000e8c41be1a167314abaf2423b72bf8da826943ffd
Arg [6] : 00000000000000000000000001051113d81d7d6da508462f2ad6d7fd96cf42ef
Arg [7] : 000000000000000000000000340759c8346a1e6ed92035fb8b6ec57ce1d82c2c
Arg [8] : 000000000000000000000000865da103d126b3be3599d84cab57109a861f5631
Arg [9] : 0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad
Arg [10] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3


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

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.