ETH Price: $2,293.92 (-7.56%)
Gas: 0.74 Gwei

Contract

0x88775b8785bAe67f4A46aB2f41d91172F100b0b9
 

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:
ExchangeWithGenericSwap

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 1 runs

Other Settings:
default evmVersion
File 1 of 35 : ExchangeWithGenericSwap.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "./ExchangeWithAtomic.sol";
import "./libs/LibGenericSwap.sol";
import "./interfaces/IAggregationExecutor.sol";

contract ExchangeWithGenericSwap is ExchangeWithAtomic {
	using SafeERC20 for IERC20;
	using SafeTransferHelper for IERC20;

	error InsufficientBalance();
	error ZeroReturnAmount();

	/// @notice Fills user's limit order through pools, delegating all calls encoded in `data` to `executor`. See tests for usage examples
	/// @param order User signed limit order
	/// @param executor Aggregation executor that executes calls described in `data`
	/// @param desc Swap description
	/// @param data Encoded calls that `caller` should execute in between of swaps
	function fillThroughPools(
		uint112 filledAmount,
		LibValidator.Order calldata order,
		IAggregationExecutor executor,
		LibValidator.SwapDescription memory desc,
		bytes calldata permit,
		bytes calldata data
	) external onlyMatcher nonReentrant {
		bytes32 orderHash = LibValidator.checkOrderSingleMatch(order, desc, filledAmount, block.timestamp);
		// if destination token is equal to fee token then fee will be fully paid inside executor contract
		if (address(desc.dstToken) != order.matcherFeeAsset) {
			payMatcherFee(order);
		} else {
			desc.minReturnAmount -= order.matcherFee; // condition desc.minReturnAmount > order.matcher fee was checked in LibValidator
		}
		LibGenericSwap.transferToInitialSource(order.senderAddress, desc, permit, assetBalances, liabilities);
		LibGenericSwap.fillThroughPools(orderHash, order.senderAddress, executor, desc, data);
		updateFilledAmount(orderHash, order.amount, filledAmount);

	}

	/// @notice Performs a swap, delegating all calls encoded in `data` to `executor`. See tests for usage examples
	/// @param executor Aggregation executor that executes calls described in `data`
	/// @param desc Swap description
	/// @param data Encoded calls that `caller` should execute in between of swaps
	/// @return returnAmount Resulting token amount
	/// @return spentAmount Source token amount
	/// @return gasLeft Gas left
	function swap(
		IAggregationExecutor executor,
		LibValidator.SwapDescription memory desc,
		bytes calldata permit,
		bytes calldata data
	) public payable nonReentrant returns (uint256 returnAmount, uint256 spentAmount, uint256 gasLeft) {
		if (desc.minReturnAmount == 0) revert ZeroReturnAmount();
		LibGenericSwap.transferToInitialSource(msg.sender, desc, permit, assetBalances, liabilities);
		(returnAmount, spentAmount, gasLeft) = LibGenericSwap.swap(msg.sender, executor, desc, data);
	}

	function payMatcherFee(LibValidator.Order memory order) internal {
		LibExchange._updateBalance(
			order.senderAddress,
			order.matcherFeeAsset,
			-int(uint(order.matcherFee)),
			assetBalances,
			liabilities
		);
		if (assetBalances[order.senderAddress][order.matcherFeeAsset] < 0) revert InsufficientBalance();
		LibExchange._updateBalance(
			order.matcherAddress,
			order.matcherFeeAsset,
			int(uint(order.matcherFee)),
			assetBalances,
			liabilities
		);
	}
}

File 2 of 35 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 3 of 35 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

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

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

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

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

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

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

File 4 of 35 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

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

File 5 of 35 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 6 of 35 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";

File 7 of 35 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

File 8 of 35 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

File 9 of 35 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @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 10 of 35 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

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

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

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

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

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

File 11 of 35 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

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

File 12 of 35 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

File 13 of 35 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

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

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

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

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

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 14 of 35 : SafeMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

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

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

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

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 15 of 35 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 16 of 35 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 17 of 35 : Exchange.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./libs/LibUnitConverter.sol";
import "./libs/LibValidator.sol";
import "./libs/LibExchange.sol";
import "./libs/MarginalFunctionality.sol";
import "./libs/SafeTransferHelper.sol";
import "./OrionVault.sol";

/**
 * @title Exchange
 * @dev Exchange contract for the Orion Protocol
 * @author @wafflemakr
 */

/*

  Overflow safety:
  We do not use SafeMath and control overflows by
  not accepting large ints on input.

  Balances inside contract are stored as int192.

  Allowed input amounts are int112 or uint112: it is enough for all
  practically used tokens: for instance if decimal unit is 1e18, int112
  allow to encode up to 2.5e15 decimal units.
  That way adding/subtracting any amount from balances won't overflow, since
  minimum number of operations to reach max int is practically infinite: ~1e24.

  Allowed prices are uint64. Note, that price is represented as
  price per 1e8 tokens. That means that amount*price always fit uint256,
  while amount*price/1e8 not only fit int192, but also can be added, subtracted
  without overflow checks: number of malicious operations to overflow ~1e13.
*/

contract Exchange is OrionVault {
	using LibValidator for LibValidator.Order;
	using SafeERC20 for IERC20;

	struct UpdateOrderBalanceData {
		uint buyType;
		uint sellType;
		int buyIn;
		int sellIn;
	}

	//  Flags for updateOrders
	//  All flags are explicit
	uint8 constant kSell = 0;
	uint8 constant kBuy = 1; //  if 0 - then sell
	uint8 constant kCorrectMatcherFeeByOrderAmount = 2;

	// EVENTS
	event NewAssetTransaction(
		address indexed beneficiary,
		address indexed recipient,
		address indexed assetAddress,
		bool isDeposit,
		uint112 amount,
		uint64 timestamp
	);

	event Trade(
		address indexed buyer,
		address indexed seller,
		address baseAsset,
		address quoteAsset,
		uint64 filledPrice,
		uint192 filledAmount,
		uint192 amountQuote,
		bytes32 tradeId
	);

	error IncorrectPosition();
	error OnlyMatcher();
	error AlreadyFilled();
	error Fallback();
	error Overflow();
	error EthDepositRejected();
	error NotCollateralAsset();

	modifier onlyMatcher() {
		if (msg.sender != _allowedMatcher) revert OnlyMatcher();
		_;
	}

	// MAIN FUNCTIONS

	/**
	 * @dev Since Exchange will work behind the Proxy contract it can not have constructor
	 */
	function initialize() public payable initializer {
		OwnableUpgradeable.__Ownable_init();
	}

	/**
	 * @dev set marginal settings
	 * @param _collateralAssets - list of addresses of assets which may be used as collateral
	 * @param _stakeRisk - risk coefficient for staked orion as uint8 (0=0, 255=1)
	 * @param _liquidationPremium - premium for liquidator as uint8 (0=0, 255=1)
	 * @param _priceOverdue - time after that price became outdated
	 * @param _positionOverdue - time after that liabilities became overdue and may be liquidated
	 */

	function updateMarginalSettings(
		address[] calldata _collateralAssets,
		uint8 _stakeRisk,
		uint8 _liquidationPremium,
		uint64 _priceOverdue,
		uint64 _positionOverdue
	) public onlyOwner {
		collateralAssets = _collateralAssets;
		stakeRisk = _stakeRisk;
		liquidationPremium = _liquidationPremium;
		priceOverdue = _priceOverdue;
		positionOverdue = _positionOverdue;
	}

	/**
	 * @dev set risk coefficients for collateral assets
	 * @param assets - list of assets
	 * @param risks - list of risks as uint8 (0=0, 255=1)
	 */
	function updateAssetRisks(address[] calldata assets, uint8[] calldata risks) public onlyOwner {
		for (uint256 i; i < assets.length; i++) assetRisks[assets[i]] = risks[i];
	}

	/**
	 * @dev Deposit ERC20 tokens to the exchange contract. Beneficiary is msg.sender
	 * @dev User needs to approve token contract first
	 * @param amount asset amount to deposit in its base unit
	 */
	function depositAsset(address assetAddress, uint112 amount) external {
		depositAssetTo(assetAddress, amount, msg.sender);
	}

	/**
	 * @dev Deposit ERC20 tokens to the exchange contract. Beneficiary is account
	 * @dev User needs to approve token contract first
	 * @param amount asset amount to deposit in its base unit
	 */
	function depositAssetTo(address assetAddress, uint112 amount, address account) public nonReentrant {
		uint256 actualAmount = IERC20(assetAddress).balanceOf(address(this));
		IERC20(assetAddress).safeTransferFrom(msg.sender, address(this), uint256(amount));
		actualAmount = IERC20(assetAddress).balanceOf(address(this)) - actualAmount;
		if (actualAmount < amount) revert NotEnoughBalance();
		generalDeposit(assetAddress, uint112(actualAmount), account);
	}

	/**
	 * @notice Deposit ETH to the exchange contract. Beneficiary is msg.sender
	 * @dev deposit event will be emitted with the amount in decimal format (10^8)
	 * @dev balance will be stored in decimal format too
	 */
	function deposit() external payable {
		depositTo(msg.sender);
	}

	/**
	 * @notice Deposit ETH to the exchange contract. Beneficiary is account
	 * @dev deposit event will be emitted with the amount in decimal format (10^8)
	 * @dev balance will be stored in decimal format too
	 */
	function depositTo(address account) public payable nonReentrant {
		generalDeposit(address(0), uint112(msg.value), account);
	}

	/**
	 * @dev internal implementation of deposits
	 */
	function generalDeposit(address assetAddress, uint112 amount, address account) internal {
		bool wasLiability = assetBalances[account][assetAddress] < 0;
		uint112 safeAmountDecimal = LibUnitConverter.baseUnitToDecimal(assetAddress, amount);
		assetBalances[account][assetAddress] += int192(uint192(safeAmountDecimal));
		emit LibExchange.BalanceChange(account, assetAddress, int192(uint192(safeAmountDecimal)));

		if (amount > 0)
			emit NewAssetTransaction(msg.sender, account, assetAddress, true, safeAmountDecimal, uint64(block.timestamp));
		if (wasLiability)
			MarginalFunctionality.updateLiability(
				account,
				assetAddress,
				liabilities,
				uint112(safeAmountDecimal),
				assetBalances[account][assetAddress]
			);
	}

	/**
	 * @dev Withdrawal of funds from the contract to provided address
	 * @param assetAddress address of the asset to withdraw
	 * @param amount asset amount to withdraw in its base unit
	 * @param to recipient address
	 */
	function withdrawTo(address assetAddress, uint112 amount, address to) public nonReentrant {
		uint112 safeAmountDecimal = LibUnitConverter.baseUnitToDecimal(assetAddress, amount);

		assetBalances[msg.sender][assetAddress] -= int192(uint192(safeAmountDecimal));
		emit LibExchange.BalanceChange(to, assetAddress, -int192(uint192(safeAmountDecimal)));

		if (assetBalances[msg.sender][assetAddress] < 0) revert NotEnoughBalance();
		if (!checkPosition(to)) revert IncorrectPosition();

		if (assetAddress == address(0)) {
			(bool success, ) = to.call{value: amount}("");
			if (!success) revert NotEnoughBalance();
		} else {
			IERC20(assetAddress).safeTransfer(to, amount);
		}

		emit NewAssetTransaction(msg.sender, to, assetAddress, false, safeAmountDecimal, uint64(block.timestamp));
	}
	/**
	 * @dev Withdrawal of remaining funds from the contract to the address
	 * @param assetAddress address of the asset to withdraw
	 * @param amount asset amount to withdraw in its base unit
	 */
	function withdraw(address assetAddress, uint112 amount) external {
		withdrawTo(assetAddress, amount, msg.sender);
	}

	/**
	 * @dev Get asset balance for a specific address
	 * @param assetAddress address of the asset to query
	 * @param user user address to query
	 */
	function getBalance(address assetAddress, address user) public view returns (int192) {
		return assetBalances[user][assetAddress];
	}

	/**
	 * @dev Batch query of asset balances for a user
	 * @param assetsAddresses array of addresses of the assets to query
	 * @param user user address to query
	 */
	function getBalances(
		address[] memory assetsAddresses,
		address user
	) public view returns (int192[] memory balances) {
		balances = new int192[](assetsAddresses.length);
		for (uint256 i; i < assetsAddresses.length; i++) {
			balances[i] = assetBalances[user][assetsAddresses[i]];
		}
	}

	/**
	 * @dev Batch query of asset liabilities for a user
	 * @param user user address to query
	 */
	function getLiabilities(
		address user
	) public view returns (MarginalFunctionality.Liability[] memory liabilitiesArray) {
		return liabilities[user];
	}

	/**
	 * @dev Return list of assets which can be used for collateral
	 */
	function getCollateralAssets() public view returns (address[] memory) {
		return collateralAssets;
	}

	/**
	 * @dev get filled amounts for a specific order
	 */
	function getFilledAmounts(
		bytes32 orderHash,
		LibValidator.Order memory order
	) public view returns (int192 totalFilled, int192 totalFeesPaid) {
		totalFilled = int192(filledAmounts[orderHash]); //It is safe to convert here: filledAmounts is result of ui112 additions
		totalFeesPaid = int192(
			uint192((uint256(order.matcherFee) * uint256(uint192(totalFilled))) / uint256(order.amount))
		); //matcherFee is u64; safe multiplication here
	}

	function updateFilledAmount(bytes32 orderHash, uint192 amount, uint112 filledBase) internal {
		uint192 total_amount = filledAmounts[orderHash];
		total_amount += filledBase; //it is safe to add ui112 to each other to get i192
		if (total_amount > amount) revert AlreadyFilled();
		filledAmounts[orderHash] = total_amount;
	}

	/**
     * @notice Settle a trade with two orders, filled price and amount
     * @dev 2 orders are submitted, it is necessary to match them:
        check conditions in orders for compliance filledPrice, filledAmount
        change balances on the contract respectively with buyer, seller, matcher
     * @param buyOrder structure of buy side order
     * @param sellOrder structure of sell side order
     * @param filledPrice price at which the order was settled
     * @param filledAmount amount settled between orders
     */
	function fillOrders(
		LibValidator.Order memory buyOrder,
		LibValidator.Order memory sellOrder,
		uint64 filledPrice,
		uint112 filledAmount
	) public nonReentrant {
		// --- VARIABLES --- //
		// Amount of quote asset
		uint256 _amountQuote = (uint256(filledAmount) * filledPrice) / (10 ** 8);
		if (_amountQuote >= type(uint112).max) revert Overflow();
		uint112 amountQuote = uint112(_amountQuote);

		// --- VALIDATIONS --- //

		// Validate signatures using eth typed sign V1
		(bytes32 buyOrderHash, bytes32 sellOrderHash) = LibValidator.checkOrdersInfo(
			buyOrder,
			sellOrder,
			msg.sender,
			filledAmount,
			filledPrice,
			block.timestamp,
			_allowedMatcher
		);

		// --- UPDATES --- //

		//updateFilledAmount
		updateFilledAmount(buyOrderHash, buyOrder.amount, filledAmount);
		updateFilledAmount(sellOrderHash, sellOrder.amount, filledAmount);

		// Update User's balances
		UpdateOrderBalanceData memory data;
		(data.buyType, data.buyIn) = LibExchange.updateOrderBalanceDebit(
			buyOrder,
			filledAmount,
			amountQuote,
			kBuy | kCorrectMatcherFeeByOrderAmount,
			assetBalances,
			liabilities
		);
		(data.sellType, data.sellIn) = LibExchange.updateOrderBalanceDebit(
			sellOrder,
			filledAmount,
			amountQuote,
			kSell | kCorrectMatcherFeeByOrderAmount,
			assetBalances,
			liabilities
		);

		LibExchange.creditUserAssets(
			data.buyType,
			buyOrder.senderAddress,
			data.buyIn,
			buyOrder.baseAsset,
			assetBalances,
			liabilities
		);
		LibExchange.creditUserAssets(
			data.sellType,
			sellOrder.senderAddress,
			data.sellIn,
			sellOrder.quoteAsset,
			assetBalances,
			liabilities
		);

		if (!checkPosition(buyOrder.senderAddress)) revert IncorrectPosition();
		if (!checkPosition(sellOrder.senderAddress)) revert IncorrectPosition();

		emit Trade(
			buyOrder.senderAddress,
			sellOrder.senderAddress,
			buyOrder.baseAsset,
			buyOrder.quoteAsset,
			filledPrice,
			filledAmount,
			amountQuote,
			keccak256(abi.encode(buyOrderHash, sellOrderHash))
		);
	}

	/**
	 * @dev wrapper for LibValidator methods, may be deleted.
	 */
	function validateOrder(LibValidator.Order memory order) public pure returns (bool isValid) {
		(isValid, ) = LibValidator.validateV3(order);
	}

	/**
	 * @dev check user marginal position (compare assets and liabilities)
	 * @return isPositive - boolean whether liabilities are covered by collateral or not
	 */
	function checkPosition(address user) public view returns (bool) {
		if (liabilities[user].length == 0) return true;
		return calcPosition(user).state == MarginalFunctionality.PositionState.POSITIVE;
	}

	/**
	 * @dev internal methods which collect all variables used by MarginalFunctionality to one structure
	 * @param user user address to query
	 * @return UsedConstants - MarginalFunctionality.UsedConstants structure
	 */
	function getConstants(address user) internal view returns (MarginalFunctionality.UsedConstants memory) {
		return
			MarginalFunctionality.UsedConstants(
				user,
				_oracleAddress,
				address(_orionToken),
				positionOverdue,
				priceOverdue,
				stakeRisk,
				liquidationPremium
			);
	}

	/**
	 * @dev calc user marginal position (compare assets and liabilities)
	 * @param user user address to query
	 * @return position - MarginalFunctionality.Position structure
	 */
	function calcPosition(address user) public view returns (MarginalFunctionality.Position memory) {
		MarginalFunctionality.UsedConstants memory constants = getConstants(user);

		return MarginalFunctionality.calcPosition(collateralAssets, liabilities, assetBalances, assetRisks, constants);
	}

	/**
     * @dev method to cover some of overdue broker liabilities and get ORN in exchange
            same as liquidation or margin call
     * @param broker - broker which will be liquidated
     * @param redeemedAsset - asset, liability of which will be covered
	 * @param collateralAsset - asset, with which liability will be covered
     * @param amount - amount of covered asset
     */
	function partiallyLiquidate(address broker, address redeemedAsset, address collateralAsset, uint112 amount) public {
		bool isCollateralAsset = false;
		for (uint i = 0; i < collateralAssets.length; ++i) {
			if (collateralAssets[i] == collateralAsset) {
				isCollateralAsset = true;
				break;
			}
		}
		if (!isCollateralAsset) revert NotCollateralAsset();
		MarginalFunctionality.UsedConstants memory constants = getConstants(broker);
		MarginalFunctionality.partiallyLiquidate(
			collateralAssets,
			liabilities,
			assetBalances,
			assetRisks,
			constants,
			redeemedAsset,
			collateralAsset,
			amount
		);
	}

	receive() external payable {
		if (msg.sender == tx.origin) revert EthDepositRejected();
	}

	/**
	 *  @dev  revert on fallback function
	 */
	fallback() external {
		revert Fallback();
	}

	/* Error Codes
        E1: Insufficient Balance, flavor S - stake, L - liabilities, P - Position, B,S - buyer, seller
        E2: Invalid Signature, flavor B,S - buyer, seller
        E3: Invalid Order Info, flavor G - general, M - wrong matcher, M2 unauthorized matcher, As - asset mismatch,
            AmB/AmS - amount mismatch (buyer,seller), PrB/PrS - price mismatch(buyer,seller), D - direction mismatch,
            U - Unit Converter Error, C - caller mismatch
        E4: Order expired, flavor B,S - buyer,seller
        E5: Contract not active,
        E6: Transfer error
        E7: Incorrect state prior to liquidation
        E8: Liquidator doesn't satisfy requirements
        E9: Data for liquidation handling is outdated
        E10: Incorrect state after liquidation
        E11: Amount overflow
        E12: Incorrect filled amount, flavor G,B,S: general(overflow), buyer order overflow, seller order overflow
        E14: Authorization error, sfs - seizeFromStake
        E15: Wrong passed params
        E16: Underlying protection mechanism error, flavor: R, I, O: Reentrancy, Initialization, Ownable
    */
}

File 18 of 35 : ExchangeStorage.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./libs/MarginalFunctionality.sol";

// Base contract which contain state variable of the first version of Exchange
// deployed on mainnet. Changes of the state variables should be introduced
// not in that contract but down the inheritance chain, to allow safe upgrades
// More info about safe upgrades here:
// https://blog.openzeppelin.com/the-state-of-smart-contract-upgrades/#upgrade-patterns

contract ExchangeStorage {
	//order -> filledAmount
	mapping(bytes32 => uint192) public filledAmounts;

	// Get user balance by address and asset address
	mapping(address => mapping(address => int192)) internal assetBalances;
	// List of assets with negative balance for each user
	mapping(address => MarginalFunctionality.Liability[]) public liabilities;
	// List of assets which can be used as collateral and risk coefficients for them
	address[] internal collateralAssets;
	mapping(address => uint8) public assetRisks;
	// Risk coefficient for locked ORN
	uint8 public stakeRisk;
	// Liquidation premium
	uint8 public liquidationPremium;
	// Delays after which price and position become outdated
	uint64 public priceOverdue;
	uint64 public positionOverdue;

	// Base orion tokens (can be locked on stake)
	IERC20 _orionToken;
	// Address of price oracle contract
	address _oracleAddress;
	// Address from which matching of orders is allowed
	address _allowedMatcher;
    //Adding gap_ due to previous OwnableUpgradeable implementation
    uint256[48] private gap_;
}

File 19 of 35 : ExchangeWithAtomic.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "./Exchange.sol";
import "./libs/LibAtomic.sol";

contract ExchangeWithAtomic is Exchange {
	uint256[2] private gap;
	address public WETH;
	mapping(bytes32 => LibAtomic.LockInfo) public atomicSwaps;
	mapping(bytes32 => bool) public secrets;

	event AtomicLocked(address sender, address asset, bytes32 secretHash);
	event AtomicRedeemed(address sender, address receiver, address asset, bytes secret);
	event AtomicClaimed(address receiver, address asset, bytes secret);
	event AtomicRefunded(address receiver, address asset, bytes32 secretHash);

	function setBasicParams(
		address orionToken,
		address priceOracleAddress,
		address allowedMatcher,
		address WETH_
	) public onlyOwner {
		_orionToken = IERC20(orionToken);
		_oracleAddress = priceOracleAddress;
		_allowedMatcher = allowedMatcher;
		WETH = WETH_;
	}

	function fillAndLockAtomic(
		LibAtomic.CrossChainOrder memory userOrder,
		LibValidator.Order memory brokerOrder,
		uint64 filledPrice,
		uint64 filledAmount,
		uint64 lockOrderExpiration
	) public onlyMatcher {
		address lockAsset;
		uint64 lockAmount;
		if (userOrder.limitOrder.buySide == 1) {
			fillOrders(userOrder.limitOrder, brokerOrder, filledPrice, filledAmount);
			lockAsset = userOrder.limitOrder.baseAsset;
			lockAmount = filledAmount;
		} else {
			fillOrders(brokerOrder, userOrder.limitOrder, filledPrice, filledAmount);
			lockAsset = userOrder.limitOrder.quoteAsset;
			lockAmount = (filledAmount * filledPrice) / 10 ** 8;
		}

		LibAtomic.LockOrder memory lockOrder = LibAtomic.LockOrder({
			sender: userOrder.limitOrder.matcherAddress,
			asset: lockAsset,
			amount: lockAmount,
			expiration: lockOrderExpiration,
			targetChainId: userOrder.chainId,
			secretHash: userOrder.secretHash
		});

		_lockAtomic(userOrder.limitOrder.senderAddress, lockOrder);
	}

	function lockAtomicByMatcher(address account, LibAtomic.LockOrder memory lockOrder) external onlyMatcher {
		_lockAtomic(account, lockOrder);
	}

	function _lockAtomic(address account, LibAtomic.LockOrder memory lockOrder) internal nonReentrant {
		LibAtomic.doLockAtomic(account, lockOrder, atomicSwaps, assetBalances, liabilities);

		if (!checkPosition(account)) revert IncorrectPosition();

		emit AtomicLocked(lockOrder.sender, lockOrder.asset, lockOrder.secretHash);
	}

	function lockAtomic(LibAtomic.LockOrder memory swap) public payable {
		_lockAtomic(msg.sender, swap);
	}

	function redeemAtomic(LibAtomic.RedeemOrder calldata order, bytes calldata secret) public {
		LibAtomic.doRedeemAtomic(order, secret, secrets, assetBalances, liabilities);
		if (!checkPosition(order.sender)) revert IncorrectPosition();

		emit AtomicRedeemed(order.sender, order.receiver, order.asset, secret);
	}

	function redeem2Atomics(
		LibAtomic.RedeemOrder calldata order1,
		bytes calldata secret1,
		LibAtomic.RedeemOrder calldata order2,
		bytes calldata secret2
	) public {
		redeemAtomic(order1, secret1);
		redeemAtomic(order2, secret2);
	}

	function claimAtomic(address receiver, bytes calldata secret, bytes calldata matcherSignature) public {
		LibAtomic.LockInfo storage swap = LibAtomic.doClaimAtomic(
			receiver,
			secret,
			matcherSignature,
			_allowedMatcher,
			atomicSwaps,
			assetBalances,
			liabilities
		);

		emit AtomicClaimed(receiver, swap.asset, secret);
	}

	function refundAtomic(bytes32 secretHash) public {
		LibAtomic.LockInfo storage swap = LibAtomic.doRefundAtomic(secretHash, atomicSwaps, assetBalances, liabilities);

		emit AtomicRefunded(swap.sender, swap.asset, secretHash);
	}

	/* Error Codes
        E1: Insufficient Balance, flavor A - Atomic, PA - Position Atomic
        E17: Incorrect atomic secret, flavor: U - used, NF - not found, R - redeemed, E/NE - expired/not expired, ETH
   */
}

File 20 of 35 : RevertReasonForwarder.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @title Revert reason forwarder.
library RevertReasonForwarder {
    /// @dev Forwards latest externall call revert.
    function reRevert() internal pure {
        // bubble up revert reason from latest external call
        assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
            let ptr := mload(0x40)
            returndatacopy(ptr, 0, returndatasize())
            revert(ptr, returndatasize())
        }
    }
}

File 21 of 35 : IAggregationExecutor.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.15;
pragma abicoder v1;

/// @title Interface for making arbitrary calls during swap
interface IAggregationExecutor {
	/// @notice Make calls on `msgSender` with specified data
	function callBytes(address msgSender, bytes calldata data) external payable; // 0x2636f7f8
}

File 22 of 35 : IDaiLikePermit.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.15;
pragma abicoder v1;

/// @title Interface for DAI-style permits
interface IDaiLikePermit {
    function permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) external;
}

File 23 of 35 : IERC20Simple.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

interface IERC20Simple {
    function balanceOf(address account) external view returns (uint256);
	function decimals() external view returns (uint8);
}

File 24 of 35 : IWETH.sol
// SPDX-License-Identifier: GNU
pragma solidity ^0.8.0;

interface IWETH {
	function deposit() external payable;

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

	function transfer(address to, uint value) external returns (bool);

	function withdraw(uint) external;
}

File 25 of 35 : LibAtomic.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./LibExchange.sol";
import "./LibValidator.sol";

library LibAtomic {
	using ECDSA for bytes32;

    struct LockOrder {
        address sender;
        uint64 expiration;
        address asset;
        uint64 amount;
        uint24 targetChainId;
        bytes32 secretHash;
    }

	struct LockInfo {
		address sender;
		uint64 expiration;
		bool used;
		address asset;
		uint64 amount;
		uint24 targetChainId;
	}

	struct ClaimOrder {
		address receiver;
		bytes32 secretHash;
	}

	struct RedeemOrder {
		address sender;
		address receiver;
		address claimReceiver;
		address asset;
		uint64 amount;
		uint64 expiration;
		bytes32 secretHash;
		bytes signature;
	}

    struct CrossChainOrder {
        LibValidator.Order limitOrder;
        uint24 chainId;
        bytes32 secretHash;
    }

	function doLockAtomic(
        address account,
		LockOrder memory swap,
		mapping(bytes32 => LockInfo) storage atomicSwaps,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => MarginalFunctionality.Liability[]) storage liabilities
	) public {
		require(swap.expiration / 1000 >= block.timestamp, "E17E");
		require(atomicSwaps[swap.secretHash].sender == address(0), "E17R");

		int remaining = int(uint(swap.amount));
		if (msg.value > 0) {
			require(swap.asset == address(0), "E17ETH");
			uint eth_sent = uint(LibUnitConverter.baseUnitToDecimal(address(0), msg.value));
			if (eth_sent < swap.amount) {
				remaining = int(uint(swap.amount)) - int(eth_sent);
			} else {
				swap.amount = uint64(eth_sent);
				remaining = 0;
			}
		}

		if (remaining > 0) {
			LibExchange._updateBalance(account, swap.asset, -1 * remaining, assetBalances, liabilities);
			require(assetBalances[account][swap.asset] >= 0, "E1A");
		}

		atomicSwaps[swap.secretHash] = LockInfo(
			swap.sender,
			swap.expiration,
			false,
			swap.asset,
			swap.amount,
			swap.targetChainId
		);
	}

	function doRedeemAtomic(
		LibAtomic.RedeemOrder calldata order,
		bytes calldata secret,
		mapping(bytes32 => bool) storage secrets,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => MarginalFunctionality.Liability[]) storage liabilities
	) public {
		require(!secrets[order.secretHash], "E17R");
		require(getEthSignedAtomicOrderHash(order).recover(order.signature) == order.sender, "E2");
		require(order.expiration / 1000 >= block.timestamp, "E4A");
		require(order.secretHash == keccak256(secret), "E17");
		secrets[order.secretHash] = true;

		LibExchange._updateBalance(order.sender, order.asset, -1 * int(uint(order.amount)), assetBalances, liabilities);

		LibExchange._updateBalance(order.receiver, order.asset, int(uint(order.amount)), assetBalances, liabilities);
	}

	function doClaimAtomic(
		address receiver,
		bytes calldata secret,
		bytes calldata matcherSignature,
		address allowedMatcher,
		mapping(bytes32 => LockInfo) storage atomicSwaps,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => MarginalFunctionality.Liability[]) storage liabilities
	) public returns (LockInfo storage swap) {
		bytes32 secretHash = keccak256(secret);
		bytes32 coHash = getEthSignedClaimOrderHash(ClaimOrder(receiver, secretHash));
		require(coHash.recover(matcherSignature) == allowedMatcher, "E2");

		swap = atomicSwaps[secretHash];
		require(swap.sender != address(0), "E17NF");
		//  require(swap.expiration/1000 >= block.timestamp, "E17E");
		require(!swap.used, "E17U");

		swap.used = true;
		LibExchange._updateBalance(receiver, swap.asset, int(uint(swap.amount)), assetBalances, liabilities);
	}

	function doRefundAtomic(
		bytes32 secretHash,
		mapping(bytes32 => LockInfo) storage atomicSwaps,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => MarginalFunctionality.Liability[]) storage liabilities
	) public returns (LockInfo storage swap) {
		swap = atomicSwaps[secretHash];
		require(swap.sender != address(0x0), "E17NF");
		require(swap.expiration / 1000 < block.timestamp, "E17NE");
		require(!swap.used, "E17U");

		swap.used = true;
		LibExchange._updateBalance(swap.sender, swap.asset, int(uint(swap.amount)), assetBalances, liabilities);
	}

	function getEthSignedAtomicOrderHash(RedeemOrder calldata _order) internal view returns (bytes32) {
		uint256 chId;
		assembly {
			chId := chainid()
		}
		return
			keccak256(
				abi.encodePacked(
					"atomicOrder",
					chId,
					_order.sender,
					_order.receiver,
					_order.claimReceiver,
					_order.asset,
					_order.amount,
					_order.expiration,
					_order.secretHash
				)
			).toEthSignedMessageHash();
	}

	function getEthSignedClaimOrderHash(ClaimOrder memory _order) internal view returns (bytes32) {
		uint256 chId;
		assembly {
			chId := chainid()
		}
		return
			keccak256(abi.encodePacked("claimOrder", chId, _order.receiver, _order.secretHash))
				.toEthSignedMessageHash();
	}
}

File 26 of 35 : LibExchange.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./MarginalFunctionality.sol";
import "./LibUnitConverter.sol";
import "./LibValidator.sol";
import "./SafeTransferHelper.sol";

library LibExchange {
	using SafeERC20 for IERC20;

	//  Flags for updateOrders
	//      All flags are explicit
	uint8 public constant kSell = 0;
	uint8 public constant kBuy = 1; //  if 0 - then sell
	uint8 public constant kCorrectMatcherFeeByOrderAmount = 2;

	event NewTrade(
		address indexed buyer,
		address indexed seller,
		address baseAsset,
		address quoteAsset,
		uint64 filledPrice,
		uint192 filledAmount,
		uint192 amountQuote
	);

	event Trade(
		address indexed buyer,
		address indexed seller,
		address baseAsset,
		address quoteAsset,
		uint64 filledPrice,
		uint192 filledAmount,
		uint192 amountQuote,
		bytes32 tradeId
	);

	event BalanceChange(address user, address asset, int192 delta);

	function _updateBalance(
		address user,
		address asset,
		int amount,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => MarginalFunctionality.Liability[]) storage liabilities
	) internal returns (uint tradeType) {
		// 0 - in contract, 1 - from wallet
		int beforeBalance = int(assetBalances[user][asset]);
		int afterBalance = beforeBalance + amount;
		require((amount >= 0 && afterBalance >= beforeBalance) || (amount < 0 && afterBalance < beforeBalance), "E11");

		if (amount > 0 && beforeBalance < 0) {
			MarginalFunctionality.updateLiability(
				user,
				asset,
				liabilities,
				uint112(uint256(amount)),
				int192(afterBalance)
			);
		} else if (beforeBalance >= 0 && afterBalance < 0) {
			if (asset != address(0)) {
				afterBalance += int(_tryDeposit(asset, uint(-1 * afterBalance), user));
			}

			// If we failed to deposit balance is still negative then we move user into liability
			if (afterBalance < 0) {
				setLiability(user, asset, int192(afterBalance), liabilities);
			} else {
				tradeType = beforeBalance > 0 ? 0 : 1;
			}
		}

		if (beforeBalance != afterBalance) {
			require(afterBalance >= type(int192).min && afterBalance <= type(int192).max, "E11");
			int192 delta = int192(afterBalance) - assetBalances[user][asset];
			assetBalances[user][asset] = int192(afterBalance);
			emit BalanceChange(user, asset, delta);
		}
	}

	/**
	 * @dev method to add liability
	 * @param user - user which created liability
	 * @param asset - liability asset
	 * @param balance - current negative balance
	 */
	function setLiability(
		address user,
		address asset,
		int192 balance,
		mapping(address => MarginalFunctionality.Liability[]) storage liabilities
	) internal {
		liabilities[user].push(
			MarginalFunctionality.Liability({
				asset: asset,
				timestamp: uint64(block.timestamp),
				outstandingAmount: uint192(-balance)
			})
		);
	}

	function _tryDeposit(address asset, uint amount, address user) internal returns (uint) {
		uint256 amountInBase = uint256(LibUnitConverter.decimalToBaseUnit(asset, amount));

		// Query allowance before trying to transferFrom
		if (
			IERC20(asset).balanceOf(user) >= amountInBase &&
			IERC20(asset).allowance(user, address(this)) >= amountInBase
		) {
			SafeERC20.safeTransferFrom(IERC20(asset), user, address(this), amountInBase);
			return amount;
		} else {
			return 0;
		}
	}

	function creditUserAssets(
		uint tradeType,
		address user,
		int amount,
		address asset,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => MarginalFunctionality.Liability[]) storage liabilities
	) internal {
		int beforeBalance = int(assetBalances[user][asset]);
		int remainingAmount = amount + beforeBalance;
		require(
			(amount >= 0 && remainingAmount >= beforeBalance) || (amount < 0 && remainingAmount < beforeBalance),
			"E11"
		);
		int sentAmount = 0;

		if (tradeType == 0 && asset == address(0) && user.balance < 1e16) {
			tradeType = 1;
		}

		if (tradeType == 1 && amount > 0 && remainingAmount > 0) {
			uint amountInBase = uint(LibUnitConverter.decimalToBaseUnit(asset, uint(amount)));
			uint contractBalance = asset == address(0) ? address(this).balance : IERC20(asset).balanceOf(address(this));
			if (contractBalance >= amountInBase) {
				SafeTransferHelper.safeTransferTokenOrETH(asset, user, amountInBase);
				sentAmount = amount;
			}
		}
		int toUpdate = amount - sentAmount;
		if (toUpdate != 0) {
			_updateBalance(user, asset, toUpdate, assetBalances, liabilities);
		}
	}

	struct SwapBalanceChanges {
		int amountOut;
		address assetOut;
		int amountIn;
		address assetIn;
	}

	/**
	 *  @notice update user balances and send matcher fee
	 *  @param flags uint8, see constants for possible flags of order
	 */
	function updateOrderBalanceDebit(
		LibValidator.Order memory order,
		uint112 amountBase,
		uint112 amountQuote,
		uint8 flags,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => MarginalFunctionality.Liability[]) storage liabilities
	) internal returns (uint tradeType, int actualIn) {
		bool isSeller = (flags & kBuy) == 0;

		{
			//  Stack too deep
			bool isCorrectFee = ((flags & kCorrectMatcherFeeByOrderAmount) != 0);

			if (isCorrectFee) {
				// matcherFee: u64, filledAmount u128 => matcherFee*filledAmount fit u256
				// result matcherFee fit u64
				order.matcherFee = uint64((uint256(order.matcherFee) * amountBase) / order.amount); //rewrite in memory only
			}
		}

		if (amountBase > 0) {
			SwapBalanceChanges memory swap;

			(swap.amountOut, swap.amountIn) = isSeller
				? (-1 * int(uint(amountBase)), int(uint(amountQuote)))
				: (-1 * int(uint(amountQuote)), int(uint(amountBase)));

			(swap.assetOut, swap.assetIn) = isSeller
				? (order.baseAsset, order.quoteAsset)
				: (order.quoteAsset, order.baseAsset);

			uint feeTradeType = 1;
			if (order.matcherFeeAsset == swap.assetOut) {
				swap.amountOut -= int(uint(order.matcherFee));
			} else if (order.matcherFeeAsset == swap.assetIn) {
				swap.amountIn -= int(uint(order.matcherFee));
			} else {
				feeTradeType = _updateBalance(
					order.senderAddress,
					order.matcherFeeAsset,
					-1 * int256(uint256(order.matcherFee)),
					assetBalances,
					liabilities
				);
			}

			tradeType =
				feeTradeType &
				_updateBalance(order.senderAddress, swap.assetOut, swap.amountOut, assetBalances, liabilities);

			actualIn = swap.amountIn;

			_updateBalance(
				order.matcherAddress,
				order.matcherFeeAsset,
				int256(uint256(order.matcherFee)),
				assetBalances,
				liabilities
			);
		}
	}
}

File 27 of 35 : LibGenericSwap.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "./LibValidator.sol";
import "./LibExchange.sol";
import "../interfaces/IAggregationExecutor.sol";
import "../utils/Errors.sol";

library LibGenericSwap {
	using SafeERC20 for IERC20;
	using SafeTransferHelper for IERC20;

	uint256 private constant _USE_EXCHANGE_BALANCE = 1 << 255;
	event OrionPoolSwap(
		address sender,
		address st,
		address rt,
		uint256 st_r,
		uint256 st_a,
		uint256 rt_r,
		uint256 rt_a,
		address f
	);

	error EthDepositRejected();
	error InsufficientReturnAmount();
	error InsufficientBalance();
	error ZeroMinReturnAmount();

	function fillThroughPools(
		bytes32 orderHash,
		address senderAddress,
		IAggregationExecutor executor,
		LibValidator.SwapDescription memory desc,
		bytes calldata data
	) external {
		uint112 filledAmount;
		uint112 quoteAmount;
		// stack too deep
		{
			(uint256 returnAmount, uint256 spentAmount, ) = swap(senderAddress, executor, desc, data);
			filledAmount = LibUnitConverter.baseUnitToDecimal(address(desc.srcToken), spentAmount);
			quoteAmount = LibUnitConverter.baseUnitToDecimal(address(desc.dstToken), returnAmount);
		}

		emit LibExchange.Trade(
			senderAddress,
			address(this),
			address(desc.srcToken),
			address(desc.dstToken),
			uint64((quoteAmount * 1e8) / filledAmount),
			filledAmount,
			quoteAmount,
			keccak256(abi.encode(orderHash, filledAmount))
		);
	}

	function swap(
		address sender,
		IAggregationExecutor executor,
		LibValidator.SwapDescription memory desc,
		bytes calldata data
	) public returns (uint256 returnAmount, uint256 spentAmount, uint256 gasLeft) {
		(uint112 amount, uint112 minReturnAmount) = (
			LibUnitConverter.decimalToBaseUnit(address(desc.srcToken), desc.amount),
			LibUnitConverter.decimalToBaseUnit(address(desc.dstToken), desc.minReturnAmount)
		);
		if (minReturnAmount == 0) revert ZeroMinReturnAmount();
		address payable dstReceiver = (desc.dstReceiver == address(0)) ? payable(sender) : desc.dstReceiver;

		returnAmount = desc.dstToken.uniBalanceOf(dstReceiver);
		_execute(sender, executor, data);
		returnAmount = desc.dstToken.uniBalanceOf(dstReceiver) - returnAmount;

		if (returnAmount < minReturnAmount) revert InsufficientReturnAmount();

		gasLeft = gasleft();
		spentAmount = amount;

		emit OrionPoolSwap(
			sender,
			address(desc.srcToken),
			address(desc.dstToken),
			spentAmount,
			spentAmount,
			returnAmount,
			returnAmount,
			address(0xA6E4Ce17474d790fb25E779F9317c55963D2cbdf)
		);
	}

	function transferToInitialSource(
		address sender,
		LibValidator.SwapDescription memory desc,
		bytes calldata permit,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => MarginalFunctionality.Liability[]) storage liabilities
	) external {
		bool srcETH = SafeTransferHelper.isETH(desc.srcToken);
		bool useExchangeBalance = desc.flags & _USE_EXCHANGE_BALANCE != 0;
		uint112 amount = LibUnitConverter.decimalToBaseUnit(address(desc.srcToken), desc.amount);

		if (!srcETH) {
			if (permit.length > 0) {
				desc.srcToken.safePermit(permit);
			}
		}

		if (useExchangeBalance) {
			if ((srcETH && (msg.value >= amount)) || (!srcETH && (msg.value != 0))) revert Errors.InvalidMsgValue();

			int updateAmount = -int(desc.amount);
			if (srcETH) {
				uint112 valueInDecimal = LibUnitConverter.baseUnitToDecimal(address(0), msg.value);
				updateAmount += int(uint(valueInDecimal));
			}
			if (updateAmount != 0) {
				LibExchange._updateBalance(sender, address(desc.srcToken), updateAmount, assetBalances, liabilities);
			}
			if (assetBalances[sender][address(desc.srcToken)] < 0) revert InsufficientBalance();

			desc.srcToken.uniTransfer(desc.srcReceiver, amount);
		} else {
			if (msg.value != (srcETH ? amount : 0)) revert Errors.InvalidMsgValue();

			if (!srcETH) {
				desc.srcToken.safeTransferFrom(sender, desc.srcReceiver, amount);
			}
		}
	}

	function _execute(address srcTokenOwner, IAggregationExecutor executor, bytes calldata data) private {
		bytes4 callBytesSelector = executor.callBytes.selector;
		assembly {
			// solhint-disable-line no-inline-assembly
			let ptr := mload(0x40)
			mstore(ptr, callBytesSelector)
			mstore(add(ptr, 0x04), srcTokenOwner)
			calldatacopy(add(ptr, 0x24), data.offset, data.length)

			if iszero(call(gas(), executor, callvalue(), ptr, add(0x24, data.length), 0, 0)) {
				returndatacopy(ptr, 0, returndatasize())
				revert(ptr, returndatasize())
			}
		}
	}
}

File 28 of 35 : LibUnitConverter.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "../interfaces/IERC20Simple.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

library LibUnitConverter {
	using SafeMath for uint;

	/**
        @notice convert asset amount from8 decimals (10^8) to its base unit
     */
	function decimalToBaseUnit(address assetAddress, uint amount) internal view returns (uint112 baseValue) {
		uint256 result;

		if (assetAddress == address(0)) {
			result = amount.mul(1 ether).div(10 ** 8); // 18 decimals
		} else {
			uint decimals = IERC20Simple(assetAddress).decimals();

			result = amount.mul(10 ** decimals).div(10 ** 8);
		}

		require(result < uint112(type(int112).max), "E3U");
		baseValue = uint112(result);
	}

	/**
        @notice convert asset amount from its base unit to 8 decimals (10^8)
     */
	function baseUnitToDecimal(address assetAddress, uint amount) internal view returns (uint112 decimalValue) {
		uint256 result;

		if (assetAddress == address(0)) {
			result = amount.mul(10 ** 8).div(1 ether);
		} else {
			uint decimals = IERC20Simple(assetAddress).decimals();

			result = amount.mul(10 ** 8).div(10 ** decimals);
		}
		require(result < uint112(type(int112).max), "E3U");
		decimalValue = uint112(result);
	}
}

File 29 of 35 : LibValidator.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";

library LibValidator {
	using ECDSA for bytes32;

	string public constant DOMAIN_NAME = "Orion Exchange";
	string public constant DOMAIN_VERSION = "1";
	uint256 public constant CHAIN_ID = 1;
	bytes32 public constant DOMAIN_SALT = 0xf2d857f4a3edcb9b78b4d503bfe733db1e3f6cdc2b7971ee739626c97e86a557;

	bytes32 public constant EIP712_DOMAIN_TYPEHASH =
		keccak256(abi.encodePacked("EIP712Domain(string name,string version,uint256 chainId,bytes32 salt)"));
	bytes32 public constant ORDER_TYPEHASH =
		keccak256(
			abi.encodePacked(
				"Order(address senderAddress,address matcherAddress,address baseAsset,address quoteAsset,address matcherFeeAsset,uint64 amount,uint64 price,uint64 matcherFee,uint64 nonce,uint64 expiration,uint8 buySide)"
			)
		);

	bytes32 public constant DOMAIN_SEPARATOR =
		keccak256(
			abi.encode(
				EIP712_DOMAIN_TYPEHASH,
				keccak256(bytes(DOMAIN_NAME)),
				keccak256(bytes(DOMAIN_VERSION)),
				CHAIN_ID,
				DOMAIN_SALT
			)
		);

	struct Order {
		address senderAddress;
		address matcherAddress;
		address baseAsset;
		address quoteAsset;
		address matcherFeeAsset;
		uint64 amount;
		uint64 price;
		uint64 matcherFee;
		uint64 nonce;
		uint64 expiration;
		uint8 buySide; // buy or sell
		bytes signature;
	}

	struct SwapDescription {
		IERC20 srcToken;
		IERC20 dstToken;
		address payable srcReceiver;
		address payable dstReceiver;
		uint256 amount;
		uint256 minReturnAmount;
		uint256 flags;
	}

	/**
	 * @dev validate order signature
	 */
	function validateV3(Order memory order) public pure returns (bool, bytes32) {
		bytes32 orderHash = getTypeValueHash(order);
		bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, orderHash));

		return (digest.recover(order.signature) == order.senderAddress, orderHash);
	}

	/**
	 * @return hash order
	 */
	function getTypeValueHash(Order memory _order) internal pure returns (bytes32) {
		return
			keccak256(
				abi.encode(
					ORDER_TYPEHASH,
					_order.senderAddress,
					_order.matcherAddress,
					_order.baseAsset,
					_order.quoteAsset,
					_order.matcherFeeAsset,
					_order.amount,
					_order.price,
					_order.matcherFee,
					_order.nonce,
					_order.expiration,
					_order.buySide
				)
			);
	}

	/**
	 * @dev basic checks of matching orders against each other
	 */
	function checkOrdersInfo(
		Order memory buyOrder,
		Order memory sellOrder,
		address sender,
		uint256 filledAmount,
		uint256 filledPrice,
		uint256 currentTime,
		address allowedMatcher
	) public pure returns (bytes32 buyOrderHash, bytes32 sellOrderHash) {
		bool isBuyOrderValid;
		bool isSellOrderValid;
		(isBuyOrderValid, buyOrderHash) = validateV3(buyOrder);
		(isSellOrderValid, sellOrderHash) = validateV3(sellOrder);
		require(isBuyOrderValid, "E2B");
		require(isSellOrderValid, "E2S");

		// Same matcher address
		require(buyOrder.matcherAddress == sender && sellOrder.matcherAddress == sender, "E3M");

		if (allowedMatcher != address(0)) {
			require(buyOrder.matcherAddress == allowedMatcher, "E3M2");
		}

		// Check matching assets
		require(buyOrder.baseAsset == sellOrder.baseAsset && buyOrder.quoteAsset == sellOrder.quoteAsset, "E3As");

		// Check order amounts
		require(filledAmount <= buyOrder.amount, "E3AmB");
		require(filledAmount <= sellOrder.amount, "E3AmS");

		// Check Price values
		require(filledPrice <= buyOrder.price, "E3");
		require(filledPrice >= sellOrder.price, "E3");

		// Check Expiration Time. Convert to seconds first
		require(buyOrder.expiration / 1000 >= currentTime, "E4B");
		require(sellOrder.expiration / 1000 >= currentTime, "E4S");

		require(buyOrder.buySide == 1 && sellOrder.buySide == 0, "E3D");
	}

	function getEthSignedOrderHash(Order memory _order) public pure returns (bytes32) {
		return
			keccak256(
				abi.encodePacked(
					"order",
					_order.senderAddress,
					_order.matcherAddress,
					_order.baseAsset,
					_order.quoteAsset,
					_order.matcherFeeAsset,
					_order.amount,
					_order.price,
					_order.matcherFee,
					_order.nonce,
					_order.expiration,
					_order.buySide
				)
			).toEthSignedMessageHash();
	}

	function checkOrderSingleMatch(
		Order memory order,
		SwapDescription memory desc,
		uint256 filledAmount,
		uint256 currentTime
	) internal pure returns (bytes32 orderHash) {
		bool isOrderValid;
		(isOrderValid, orderHash) = validateV3(order);
		require(isOrderValid, "E2B");

		uint256 amountQuote = (uint256(filledAmount) * order.price) / 10 ** 8;

		uint256 amount_spend;
		uint256 amount_receive;
		if (order.buySide == 1) {
			require(order.quoteAsset == address(desc.srcToken) && order.baseAsset == address(desc.dstToken), "E3As");
			(amount_spend, amount_receive) = (amountQuote, filledAmount);
		} else {
			require(order.baseAsset == address(desc.srcToken) && order.quoteAsset == address(desc.dstToken), "E3As");
			(amount_spend, amount_receive) = (filledAmount, amountQuote);
		}

		require(order.senderAddress == desc.dstReceiver, "IncorrectReceiver");
		require(amount_spend == desc.amount, "IncorrectAmount");
		require(amount_receive >= desc.minReturnAmount, "IncorrectAmount");
		require(filledAmount <= order.amount, "E3AmB");
		require(order.expiration / 1000 >= currentTime, "E4B");
		if (address(desc.dstToken) == order.matcherFeeAsset) {
			require(desc.minReturnAmount > order.matcherFee);
		}
	}
}

File 30 of 35 : MarginalFunctionality.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;
pragma experimental ABIEncoderV2;
import "../PriceOracleInterface.sol";
import "./LibExchange.sol";

library MarginalFunctionality {
	// We have the following approach: when liability is created we store
	// timestamp and size of liability. If the subsequent trade will deepen
	// this liability or won't fully cover it timestamp will not change.
	// However once outstandingAmount is covered we check whether balance on
	// that asset is positive or not. If not, liability still in the place but
	// time counter is dropped and timestamp set to `now`.
	struct Liability {
		address asset;
		uint64 timestamp;
		uint192 outstandingAmount;
	}

	enum PositionState {
		POSITIVE,
		NEGATIVE, // weighted position below 0
		OVERDUE, // liability is not returned for too long
		NOPRICE, // some assets has no price or expired
		INCORRECT // some of the basic requirements are not met: too many liabilities, no locked stake, etc
	}

	struct Position {
		PositionState state;
		int256 weightedPosition; // sum of weighted collateral minus liabilities
		int256 totalPosition; // sum of unweighted (total) collateral minus liabilities
		int256 totalLiabilities; // total liabilities value
	}

	// Constants from Exchange contract used for calculations
	struct UsedConstants {
		address user;
		address _oracleAddress;
		address _orionTokenAddress;
		uint64 positionOverdue;
		uint64 priceOverdue;
		uint8 stakeRisk;
		uint8 liquidationPremium;
	}

	/**
	 * @dev method to multiply numbers with uint8 based percent numbers
	 */
	function uint8Percent(int192 _a, uint8 _b) internal pure returns (int192 c) {
		int a = int256(_a);
		int b = int256(uint256(_b));
		int d = 255;
		c = int192((a > 65536) ? (a / d) * b : (a * b) / d);
	}

	/**
	 * @dev method to fetch asset prices in ORN tokens
	 */
	function getAssetPrice(address asset, address oracle) internal view returns (uint64 price, uint64 timestamp) {
		PriceOracleInterface.PriceDataOut memory assetPriceData = PriceOracleInterface(oracle).assetPrices(asset);
		(price, timestamp) = (assetPriceData.price, assetPriceData.timestamp);
	}

	/**
     * @dev method to calc weighted and absolute collateral value
     * @notice it only count for assets in collateralAssets list, all other
               assets will add 0 to position.
     * @return outdated whether any price is outdated
     * @return weightedPosition in ORN
     * @return totalPosition in ORN
     */
	function calcAssets(
		address[] storage collateralAssets,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => uint8) storage assetRisks,
		address user,
		address orionTokenAddress,
		address oracleAddress,
		uint64 priceOverdue
	) internal view returns (bool outdated, int192 weightedPosition, int192 totalPosition) {
		uint256 collateralAssetsLength = collateralAssets.length;
		for (uint256 i = 0; i < collateralAssetsLength; i++) {
			address asset = collateralAssets[i];
			if (assetBalances[user][asset] < 0) continue; // will be calculated in calcLiabilities
			(uint64 price, uint64 timestamp) = (1e8, 0xfffffff000000000);

			if (asset != orionTokenAddress) {
				(price, timestamp) = getAssetPrice(asset, oracleAddress);
			}

			// balance: i192, price u64 => balance*price fits i256
			// since generally balance <= N*maxInt112 (where N is number operations with it),
			// assetValue <= N*maxInt112*maxUInt64/1e8.
			// That is if N<= 2**17 *1e8 = 1.3e13  we can neglect overflows here

			uint8 specificRisk = assetRisks[asset];
			int192 balance = assetBalances[user][asset];
			int256 _assetValue = (int256(balance) * int256(uint256(price))) / 1e8;
			int192 assetValue = int192(_assetValue);

			// Overflows logic holds here as well, except that N is the number of
			// operations for all assets

			if (assetValue > 0) {
				weightedPosition += uint8Percent(assetValue, specificRisk);
				totalPosition += assetValue;
				outdated = outdated || ((timestamp + priceOverdue) < block.timestamp);
			}
		}

		return (outdated, weightedPosition, totalPosition);
	}

	/**
	 * @dev method to calc liabilities
	 * @return outdated whether any price is outdated
	 * @return overdue whether any liability is overdue
	 * @return weightedPosition weightedLiability == totalLiability in ORN
	 * @return totalPosition totalLiability in ORN
	 */
	function calcLiabilities(
		mapping(address => Liability[]) storage liabilities,
		mapping(address => mapping(address => int192)) storage assetBalances,
		address user,
		address oracleAddress,
		uint64 positionOverdue,
		uint64 priceOverdue
	) internal view returns (bool outdated, bool overdue, int192 weightedPosition, int192 totalPosition) {
		uint256 liabilitiesLength = liabilities[user].length;

		for (uint256 i = 0; i < liabilitiesLength; i++) {
			Liability storage liability = liabilities[user][i];
			int192 balance = assetBalances[user][liability.asset];
			(uint64 price, uint64 timestamp) = getAssetPrice(liability.asset, oracleAddress);
			// balance: i192, price u64 => balance*price fits i256
			// since generally balance <= N*maxInt112 (where N is number operations with it),
			// assetValue <= N*maxInt112*maxUInt64/1e8.
			// That is if N<= 2**17 *1e8 = 1.3e13  we can neglect overflows here

			int192 liabilityValue = int192((int256(balance) * int256(uint256(price))) / 1e8);
			weightedPosition += liabilityValue; //already negative since balance is negative
			totalPosition += liabilityValue;
			overdue = overdue || ((liability.timestamp + positionOverdue) < block.timestamp);
			outdated = outdated || ((timestamp + priceOverdue) < block.timestamp);
		}

		return (outdated, overdue, weightedPosition, totalPosition);
	}

	/**
	 * @dev method to calc Position
	 * @return result position structure
	 */
	function calcPosition(
		address[] storage collateralAssets,
		mapping(address => Liability[]) storage liabilities,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => uint8) storage assetRisks,
		UsedConstants memory constants
	) public view returns (Position memory result) {
		(bool outdatedPrice, int192 weightedPosition, int192 totalPosition) = calcAssets(
			collateralAssets,
			assetBalances,
			assetRisks,
			constants.user,
			constants._orionTokenAddress,
			constants._oracleAddress,
			constants.priceOverdue
		);

		(bool _outdatedPrice, bool overdue, int192 _weightedPosition, int192 _totalPosition) = calcLiabilities(
			liabilities,
			assetBalances,
			constants.user,
			constants._oracleAddress,
			constants.positionOverdue,
			constants.priceOverdue
		);

		weightedPosition += _weightedPosition;
		totalPosition += _totalPosition;
		outdatedPrice = outdatedPrice || _outdatedPrice;
		if (_totalPosition < 0) {
			result.totalLiabilities = _totalPosition;
		}
		if (weightedPosition < 0) {
			result.state = PositionState.NEGATIVE;
		}
		if (outdatedPrice) {
			result.state = PositionState.NOPRICE;
		}
		if (overdue) {
			result.state = PositionState.OVERDUE;
		}
		result.weightedPosition = weightedPosition;
		result.totalPosition = totalPosition;
	}

	/**
	 * @dev method removes liability
	 */
	function removeLiability(address user, address asset, mapping(address => Liability[]) storage liabilities) public {
		uint256 length = liabilities[user].length;

		for (uint256 i = 0; i < length; i++) {
			if (liabilities[user][i].asset == asset) {
				if (length > 1) {
					liabilities[user][i] = liabilities[user][length - 1];
				}
				liabilities[user].pop();
				break;
			}
		}
	}

	/**
	 * @dev method update liability
	 * @notice implement logic for outstandingAmount (see Liability description)
	 */
	function updateLiability(
		address user,
		address asset,
		mapping(address => Liability[]) storage liabilities,
		uint112 depositAmount,
		int192 currentBalance
	) internal {
		if (currentBalance >= 0) {
			removeLiability(user, asset, liabilities);
		} else {
			uint256 i;
			uint256 liabilitiesLength = liabilities[user].length;
			for (; i < liabilitiesLength - 1; i++) {
				if (liabilities[user][i].asset == asset) break;
			}
			Liability storage liability = liabilities[user][i];
			if (depositAmount >= liability.outstandingAmount) {
				liability.outstandingAmount = uint192(-currentBalance);
				liability.timestamp = uint64(block.timestamp);
			} else {
				liability.outstandingAmount -= depositAmount;
			}
		}
	}

	/**
     * @dev partially liquidate, that is cover some asset liability to get
            ORN from misbehavior broker
     */
	function partiallyLiquidate(
		address[] storage collateralAssets,
		mapping(address => Liability[]) storage liabilities,
		mapping(address => mapping(address => int192)) storage assetBalances,
		mapping(address => uint8) storage assetRisks,
		UsedConstants memory constants,
		address redeemedAsset,
		address collateralAsset,
		uint112 amount
	) public {
		//Note: constants.user - is broker who will be liquidated
		Position memory initialPosition = calcPosition(
			collateralAssets,
			liabilities,
			assetBalances,
			assetRisks,
			constants
		);
		require(
			initialPosition.state == PositionState.NEGATIVE || initialPosition.state == PositionState.OVERDUE,
			"E7"
		);
		address liquidator = msg.sender;
		require(assetBalances[liquidator][redeemedAsset] >= int192(uint192(amount)), "E8");
		require(assetBalances[constants.user][redeemedAsset] < 0, "E15");
		assetBalances[liquidator][redeemedAsset] -= int192(uint192(amount));
		assetBalances[constants.user][redeemedAsset] += int192(uint192(amount));

		emit LibExchange.BalanceChange(liquidator, redeemedAsset, -int192(uint192(amount)));
		emit LibExchange.BalanceChange(constants.user, redeemedAsset, int192(uint192(amount)));

		if (assetBalances[constants.user][redeemedAsset] >= 0)
			removeLiability(constants.user, redeemedAsset, liabilities);

		(uint64 price, uint64 ts1) = getAssetPrice(redeemedAsset, constants._oracleAddress);
		require(ts1 + constants.priceOverdue > block.timestamp, "E9"); //Price is outdated

		if (collateralAsset != constants._orionTokenAddress) { //
			(uint64 collateralPrice, uint64 ts2) = getAssetPrice(collateralAsset, constants._oracleAddress);
			require(ts2 + constants.priceOverdue > block.timestamp, "E9"); //Price is outdated
			price = (price * 1e8) / collateralPrice;
		}

		reimburseLiquidator(
			amount,
			price,
			collateralAsset,
			liquidator,
			assetBalances,
			constants.liquidationPremium,
			constants.user
		);

		Position memory finalPosition = calcPosition(
			collateralAssets,
			liabilities,
			assetBalances,
			assetRisks,
			constants
		);
		require(
			uint(finalPosition.state) < 3 && //POSITIVE,NEGATIVE or OVERDUE
				(finalPosition.weightedPosition > initialPosition.weightedPosition),
			"E10"
		); //Incorrect state position after liquidation
		if (finalPosition.state == PositionState.POSITIVE)
			require(finalPosition.weightedPosition < 10e8, "Can not liquidate to very positive state");
	}

	/**
	 * @dev reimburse liquidator with collateral: first from stake, than from broker balance
	 */
	function reimburseLiquidator(
		uint112 amount,
		uint64 price,
		address collateralAsset,
		address liquidator,
		mapping(address => mapping(address => int192)) storage assetBalances,
		uint8 liquidationPremium,
		address user
	) internal {
		int192 collateralAmount = int192((int256(uint256(amount)) * int256(uint256(price))) / 1e8);
		collateralAmount += uint8Percent(collateralAmount, liquidationPremium); //Liquidation premium

		int192 onBalanceCollateral = assetBalances[user][collateralAsset];

		require(onBalanceCollateral >= collateralAmount, "E10");
		assetBalances[user][collateralAsset] -= collateralAmount;
		assetBalances[liquidator][collateralAsset] += collateralAmount;

		emit LibExchange.BalanceChange(user, collateralAsset, -collateralAmount);
		emit LibExchange.BalanceChange(liquidator, collateralAsset, collateralAmount);
	}
}

File 31 of 35 : SafeTransferHelper.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "../interfaces/IWETH.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../helpers/RevertReasonForwarder.sol";
import "../interfaces/IDaiLikePermit.sol";


library SafeTransferHelper {
	using SafeERC20 for IERC20;

	error InsufficientBalance();
	error ForceApproveFailed();
	error ApproveCalledOnETH();
	error NotEnoughValue();
	error FromIsNotSender();
	error ToIsNotThis();
	error ETHTransferFailed();
	error SafePermitBadLength();

	uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
	IERC20 private constant _ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
	IERC20 private constant _ZERO_ADDRESS = IERC20(address(0));

	/// @dev Returns true if `token` is ETH.
	function isETH(IERC20 token) internal pure returns (bool) {
		return (token == _ZERO_ADDRESS || token == _ETH_ADDRESS);
	}

	/// @dev Returns `account` ERC20 `token` balance.
	function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) {
		if (isETH(token)) {
			return account.balance;
		} else {
			return token.balanceOf(account);
		}
	}

	/// @dev `token` transfer `to` `amount`.
	/// Note that this function does nothing in case of zero amount.
	/// @dev `token` transfer `to` `amount`.
	/// Note that this function does nothing in case of zero amount.
	function uniTransfer(IERC20 token, address payable to, uint256 amount) internal {
		if (amount > 0) {
			if (isETH(token)) {
				if (address(this).balance < amount) revert InsufficientBalance();
				// solhint-disable-next-line avoid-low-level-calls
				(bool success, ) = to.call{value: amount, gas: _RAW_CALL_GAS_LIMIT}("");
				if (!success) revert ETHTransferFailed();
			} else {
				token.safeTransfer(to, amount);
			}
		}
	}

	/// @dev Reverts if `token` is ETH, otherwise performs ERC20 forceApprove.
	function uniApprove(IERC20 token, address to, uint256 amount) internal {
		if (isETH(token)) revert ApproveCalledOnETH();

		forceApprove(token, to, amount);
	}

	/// @dev If `approve(from, to, amount)` fails, try to `approve(from, to, 0)` before retry.
	function forceApprove(IERC20 token, address spender, uint256 value) internal {
		if (!_makeCall(token, token.approve.selector, spender, value)) {
			if (
				!_makeCall(token, token.approve.selector, spender, 0) ||
				!_makeCall(token, token.approve.selector, spender, value)
			) {
				revert ForceApproveFailed();
			}
		}
	}

	function safeAutoTransferFrom(address weth, address token, address from, address to, uint value) internal {
		if (isETH(IERC20(token))) {
			require(from == address(this), "TransferFrom: this");
			IWETH(weth).deposit{value: value}();
			assert(IWETH(weth).transfer(to, value));
		} else {
			if (from == address(this)) {
				SafeERC20.safeTransfer(IERC20(token), to, value);
			} else {
				SafeERC20.safeTransferFrom(IERC20(token), from, to, value);
			}
		}
	}

	function safeAutoTransferTo(address weth, address token, address to, uint value) internal {
		if (address(this) != to) {
			if (isETH(IERC20(token))) {
				IWETH(weth).withdraw(value);
				Address.sendValue(payable(to), value);
			} else {
				SafeERC20.safeTransfer(IERC20(token), to, value);
			}
		}
	}

	function safeTransferTokenOrETH(address token, address to, uint value) internal {
		if (value > 0) {
			if (isETH(IERC20(token))) {
				if (address(this).balance < value) revert InsufficientBalance();
				// solhint-disable-next-line avoid-low-level-calls
				(bool success, ) = to.call{value: value, gas: _RAW_CALL_GAS_LIMIT}("");
				if (!success) revert ETHTransferFailed();
			} else {
				IERC20(token).safeTransfer(to, value);
			}
		}
	}

	function safePermit(IERC20 token, bytes calldata permit) internal {
		bool success;
		if (permit.length == 32 * 7) {
			// solhint-disable-next-line avoid-low-level-calls
			success = _makeCalldataCall(token, IERC20Permit.permit.selector, permit);
		} else if (permit.length == 32 * 8) {
			// solhint-disable-next-line avoid-low-level-calls
			success = _makeCalldataCall(token, IDaiLikePermit.permit.selector, permit);
		} else {
			revert SafePermitBadLength();
		}

		if (!success) {
			RevertReasonForwarder.reRevert();
		}
	}

    function _makeCall(IERC20 token, bytes4 selector, address to, uint256 amount) private returns (bool success) {
		assembly ("memory-safe") {
			// solhint-disable-line no-inline-assembly
			let data := mload(0x40)

			mstore(data, selector)
			mstore(add(data, 0x04), to)
			mstore(add(data, 0x24), amount)
			success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
			if success {
				switch returndatasize()
				case 0 {
					success := gt(extcodesize(token), 0)
				}
				default {
					success := and(gt(returndatasize(), 31), eq(mload(0), 1))
				}
			}
		}
	}

	function _makeCalldataCall(IERC20 token, bytes4 selector, bytes calldata args) private returns (bool done) {
		/// @solidity memory-safe-assembly
		assembly {
			// solhint-disable-line no-inline-assembly
			let len := add(4, args.length)
			let data := mload(0x40)

			mstore(data, selector)
			calldatacopy(add(data, 0x04), args.offset, args.length)
			let success := call(gas(), token, 0, data, len, 0x0, 0x20)
			done := and(success, or(iszero(returndatasize()), and(gt(returndatasize(), 31), eq(mload(0), 1))))
		}
	}
}

File 32 of 35 : OrionVault.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./ExchangeStorage.sol";
import "./libs/LibExchange.sol";

abstract contract OrionVault is ExchangeStorage, ReentrancyGuard, OwnableUpgradeable {
    error NotEnoughBalance();
	enum StakePhase {
		NOTSTAKED,
		LOCKED,
		RELEASING,
		READYTORELEASE,
		FROZEN
	}

	struct Stake {
		uint64 amount; // 100m ORN in circulation fits uint64
		StakePhase phase;
		uint64 lastActionTimestamp;
	}

	uint64 constant releasingDuration = 3600 * 24;
	mapping(address => Stake) private stakingData;

	/**
	 * @dev Returns locked or frozen stake balance only
	 * @param user address
	 */
	function getLockedStakeBalance(address user) public view returns (uint256) {
		return stakingData[user].amount;
	}

	/**
	 * @dev Request stake unlock for msg.sender
	 * @dev If stake phase is LOCKED, that changes phase to RELEASING
	 * @dev If stake phase is READYTORELEASE, that withdraws stake to balance
	 * @dev Note, both unlock and withdraw is impossible if user has liabilities
	 */
	function requestReleaseStake() public {
		address user = _msgSender();
		Stake storage stake = stakingData[user];
		assetBalances[user][address(_orionToken)] += int192(uint192(stake.amount));
		emit LibExchange.BalanceChange(user, address(_orionToken), int192(uint192(stake.amount)));
		stake.amount = 0;
		stake.phase = StakePhase.NOTSTAKED;
	}

	/**
	 * @dev Lock some orions from exchange balance sheet
	 * @param amount orions in 1e-8 units to stake
	 */
	function lockStake(uint64 amount) public {
		address user = _msgSender();
		if (assetBalances[user][address(_orionToken)] < int192(uint192(amount))) revert NotEnoughBalance();
		Stake storage stake = stakingData[user];

		assetBalances[user][address(_orionToken)] -= int192(uint192(amount));
		emit LibExchange.BalanceChange(user, address(_orionToken), -int192(uint192(amount)));

		stake.amount += amount;
	}
}

File 33 of 35 : PriceOracleDataTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

interface PriceOracleDataTypes {
	struct PriceDataOut {
		uint64 price;
		uint64 timestamp;
	}
}

File 34 of 35 : PriceOracleInterface.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "./PriceOracleDataTypes.sol";

interface PriceOracleInterface is PriceOracleDataTypes {
	function assetPrices(address) external view returns (PriceDataOut memory);

	function givePrices(address[] calldata assetAddresses) external view returns (PriceDataOut[] memory);
}

File 35 of 35 : Errors.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.15;
pragma abicoder v1;

library Errors {
	error ReturnAmountIsNotEnough();
	error InvalidMsgValue();
	error ERC20TransferFailed();
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {
    "contracts/libs/LibAtomic.sol": {
      "LibAtomic": "0x59723329a1e906629516b30ca9c30400b11a1ecd"
    },
    "contracts/libs/LibGenericSwap.sol": {
      "LibGenericSwap": "0x4a7aa0fcf9b35d87b4125c90484497bb2e050e85"
    },
    "contracts/libs/LibValidator.sol": {
      "LibValidator": "0xe8c2ac36f9e689f0b4b7c52d80b42fdfbd664567"
    },
    "contracts/libs/MarginalFunctionality.sol": {
      "MarginalFunctionality": "0xdc91af7b038cc8760a0c53814f21be09caace0d7"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"AlreadyFilled","type":"error"},{"inputs":[],"name":"ETHTransferFailed","type":"error"},{"inputs":[],"name":"EthDepositRejected","type":"error"},{"inputs":[],"name":"Fallback","type":"error"},{"inputs":[],"name":"IncorrectPosition","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"NotCollateralAsset","type":"error"},{"inputs":[],"name":"NotEnoughBalance","type":"error"},{"inputs":[],"name":"OnlyMatcher","type":"error"},{"inputs":[],"name":"Overflow","type":"error"},{"inputs":[],"name":"ZeroReturnAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"bytes","name":"secret","type":"bytes"}],"name":"AtomicClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"bytes32","name":"secretHash","type":"bytes32"}],"name":"AtomicLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"bytes","name":"secret","type":"bytes"}],"name":"AtomicRedeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"bytes32","name":"secretHash","type":"bytes32"}],"name":"AtomicRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"assetAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"isDeposit","type":"bool"},{"indexed":false,"internalType":"uint112","name":"amount","type":"uint112"},{"indexed":false,"internalType":"uint64","name":"timestamp","type":"uint64"}],"name":"NewAssetTransaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"address","name":"baseAsset","type":"address"},{"indexed":false,"internalType":"address","name":"quoteAsset","type":"address"},{"indexed":false,"internalType":"uint64","name":"filledPrice","type":"uint64"},{"indexed":false,"internalType":"uint192","name":"filledAmount","type":"uint192"},{"indexed":false,"internalType":"uint192","name":"amountQuote","type":"uint192"},{"indexed":false,"internalType":"bytes32","name":"tradeId","type":"bytes32"}],"name":"Trade","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"assetRisks","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"atomicSwaps","outputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"bool","name":"used","type":"bool"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint24","name":"targetChainId","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"calcPosition","outputs":[{"components":[{"internalType":"enum MarginalFunctionality.PositionState","name":"state","type":"uint8"},{"internalType":"int256","name":"weightedPosition","type":"int256"},{"internalType":"int256","name":"totalPosition","type":"int256"},{"internalType":"int256","name":"totalLiabilities","type":"int256"}],"internalType":"struct MarginalFunctionality.Position","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"checkPosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"secret","type":"bytes"},{"internalType":"bytes","name":"matcherSignature","type":"bytes"}],"name":"claimAtomic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint112","name":"amount","type":"uint112"}],"name":"depositAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"address","name":"account","type":"address"}],"name":"depositAssetTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"depositTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"matcherAddress","type":"address"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"address","name":"quoteAsset","type":"address"},{"internalType":"address","name":"matcherFeeAsset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"price","type":"uint64"},{"internalType":"uint64","name":"matcherFee","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint8","name":"buySide","type":"uint8"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibValidator.Order","name":"limitOrder","type":"tuple"},{"internalType":"uint24","name":"chainId","type":"uint24"},{"internalType":"bytes32","name":"secretHash","type":"bytes32"}],"internalType":"struct LibAtomic.CrossChainOrder","name":"userOrder","type":"tuple"},{"components":[{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"matcherAddress","type":"address"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"address","name":"quoteAsset","type":"address"},{"internalType":"address","name":"matcherFeeAsset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"price","type":"uint64"},{"internalType":"uint64","name":"matcherFee","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint8","name":"buySide","type":"uint8"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibValidator.Order","name":"brokerOrder","type":"tuple"},{"internalType":"uint64","name":"filledPrice","type":"uint64"},{"internalType":"uint64","name":"filledAmount","type":"uint64"},{"internalType":"uint64","name":"lockOrderExpiration","type":"uint64"}],"name":"fillAndLockAtomic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"matcherAddress","type":"address"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"address","name":"quoteAsset","type":"address"},{"internalType":"address","name":"matcherFeeAsset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"price","type":"uint64"},{"internalType":"uint64","name":"matcherFee","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint8","name":"buySide","type":"uint8"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibValidator.Order","name":"buyOrder","type":"tuple"},{"components":[{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"matcherAddress","type":"address"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"address","name":"quoteAsset","type":"address"},{"internalType":"address","name":"matcherFeeAsset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"price","type":"uint64"},{"internalType":"uint64","name":"matcherFee","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint8","name":"buySide","type":"uint8"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibValidator.Order","name":"sellOrder","type":"tuple"},{"internalType":"uint64","name":"filledPrice","type":"uint64"},{"internalType":"uint112","name":"filledAmount","type":"uint112"}],"name":"fillOrders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint112","name":"filledAmount","type":"uint112"},{"components":[{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"matcherAddress","type":"address"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"address","name":"quoteAsset","type":"address"},{"internalType":"address","name":"matcherFeeAsset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"price","type":"uint64"},{"internalType":"uint64","name":"matcherFee","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint8","name":"buySide","type":"uint8"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibValidator.Order","name":"order","type":"tuple"},{"internalType":"contract IAggregationExecutor","name":"executor","type":"address"},{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"dstToken","type":"address"},{"internalType":"address payable","name":"srcReceiver","type":"address"},{"internalType":"address payable","name":"dstReceiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturnAmount","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"}],"internalType":"struct LibValidator.SwapDescription","name":"desc","type":"tuple"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"fillThroughPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"filledAmounts","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"address","name":"user","type":"address"}],"name":"getBalance","outputs":[{"internalType":"int192","name":"","type":"int192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetsAddresses","type":"address[]"},{"internalType":"address","name":"user","type":"address"}],"name":"getBalances","outputs":[{"internalType":"int192[]","name":"balances","type":"int192[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollateralAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"components":[{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"matcherAddress","type":"address"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"address","name":"quoteAsset","type":"address"},{"internalType":"address","name":"matcherFeeAsset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"price","type":"uint64"},{"internalType":"uint64","name":"matcherFee","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint8","name":"buySide","type":"uint8"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibValidator.Order","name":"order","type":"tuple"}],"name":"getFilledAmounts","outputs":[{"internalType":"int192","name":"totalFilled","type":"int192"},{"internalType":"int192","name":"totalFeesPaid","type":"int192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getLiabilities","outputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint192","name":"outstandingAmount","type":"uint192"}],"internalType":"struct MarginalFunctionality.Liability[]","name":"liabilitiesArray","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getLockedStakeBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"liabilities","outputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint192","name":"outstandingAmount","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidationPremium","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint24","name":"targetChainId","type":"uint24"},{"internalType":"bytes32","name":"secretHash","type":"bytes32"}],"internalType":"struct LibAtomic.LockOrder","name":"swap","type":"tuple"}],"name":"lockAtomic","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint24","name":"targetChainId","type":"uint24"},{"internalType":"bytes32","name":"secretHash","type":"bytes32"}],"internalType":"struct LibAtomic.LockOrder","name":"lockOrder","type":"tuple"}],"name":"lockAtomicByMatcher","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"amount","type":"uint64"}],"name":"lockStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"broker","type":"address"},{"internalType":"address","name":"redeemedAsset","type":"address"},{"internalType":"address","name":"collateralAsset","type":"address"},{"internalType":"uint112","name":"amount","type":"uint112"}],"name":"partiallyLiquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"positionOverdue","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceOverdue","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"claimReceiver","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"bytes32","name":"secretHash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibAtomic.RedeemOrder","name":"order1","type":"tuple"},{"internalType":"bytes","name":"secret1","type":"bytes"},{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"claimReceiver","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"bytes32","name":"secretHash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibAtomic.RedeemOrder","name":"order2","type":"tuple"},{"internalType":"bytes","name":"secret2","type":"bytes"}],"name":"redeem2Atomics","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"claimReceiver","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"bytes32","name":"secretHash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibAtomic.RedeemOrder","name":"order","type":"tuple"},{"internalType":"bytes","name":"secret","type":"bytes"}],"name":"redeemAtomic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"secretHash","type":"bytes32"}],"name":"refundAtomic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestReleaseStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"secrets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"orionToken","type":"address"},{"internalType":"address","name":"priceOracleAddress","type":"address"},{"internalType":"address","name":"allowedMatcher","type":"address"},{"internalType":"address","name":"WETH_","type":"address"}],"name":"setBasicParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeRisk","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IAggregationExecutor","name":"executor","type":"address"},{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"dstToken","type":"address"},{"internalType":"address payable","name":"srcReceiver","type":"address"},{"internalType":"address payable","name":"dstReceiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturnAmount","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"}],"internalType":"struct LibValidator.SwapDescription","name":"desc","type":"tuple"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"gasLeft","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint8[]","name":"risks","type":"uint8[]"}],"name":"updateAssetRisks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_collateralAssets","type":"address[]"},{"internalType":"uint8","name":"_stakeRisk","type":"uint8"},{"internalType":"uint8","name":"_liquidationPremium","type":"uint8"},{"internalType":"uint64","name":"_priceOverdue","type":"uint64"},{"internalType":"uint64","name":"_positionOverdue","type":"uint64"}],"name":"updateMarginalSettings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"matcherAddress","type":"address"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"address","name":"quoteAsset","type":"address"},{"internalType":"address","name":"matcherFeeAsset","type":"address"},{"internalType":"uint64","name":"amount","type":"uint64"},{"internalType":"uint64","name":"price","type":"uint64"},{"internalType":"uint64","name":"matcherFee","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint8","name":"buySide","type":"uint8"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibValidator.Order","name":"order","type":"tuple"}],"name":"validateOrder","outputs":[{"internalType":"bool","name":"isValid","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint112","name":"amount","type":"uint112"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b506001603955615fb380620000266000396000f3fe6080604052600436106102065760003560e01c806311fbc3091461025357806312aa3caf1461029657806315ba55e5146102c457806318044e80146102e4578063253d73a81461030457806328d0a326146103245780632b3a54411461034b57806340f1a34d1461036b5780634e91d2c6146103b9578063548dd437146103d957806355664d37146103ee5780636241d7681461043b57806362a3f4dd1461047d5780636628b4641461049d5780636c3175bb146104bd578063715018a6146104d05780638129fc1c146104e55780638293e9af146104ed5780638795e1bb1461050d57806388a0ec621461052d5780638da5cb5b1461054d578063963ad20c1461056f578063a02fbb781461058f578063a0a90856146105af578063a1ff9bee1461066f578063a5ffc19a14610691578063a9d5ec76146106cb578063ad5c4648146106eb578063b0d3b87e1461070b578063b760faf91461072b578063c18c9d931461073e578063d0e30db01461076e578063d1c0702814610776578063d4fac45d146107a3578063dcffd7cb146107d6578063e0c3ebcf146107f0578063e326dbbf14610810578063e4fd7d0114610830578063e658defb14610850578063e6b8b82514610870578063ef74e5941461088f578063f2fde38b146108bf578063f721599e146108df578063f7522af61461090c578063ff782d9b146109625761022d565b3661022d5732330361022b57604051631b10b0f960e01b815260040160405180910390fd5b005b34801561023957600080fd5b506040516314ad190f60e21b815260040160405180910390fd5b34801561025f57600080fd5b50600554610279906201000090046001600160401b031681565b6040516001600160401b0390911681526020015b60405180910390f35b6102a96102a4366004614474565b61098f565b6040805193845260208401929092529082015260600161028d565b3480156102d057600080fd5b5061022b6102df3660046146c5565b610ad1565b3480156102f057600080fd5b5061022b6102ff3660046147be565b610bde565b34801561031057600080fd5b5061022b61031f36600461483d565b610cef565b34801561033057600080fd5b5060055461027990600160501b90046001600160401b031681565b34801561035757600080fd5b5061022b610366366004614919565b610ee4565b34801561037757600080fd5b506103a16103863660046149b2565b6000602081905290815260409020546001600160c01b031681565b6040516001600160c01b03909116815260200161028d565b3480156103c557600080fd5b5061022b6103d43660046149cb565b610f02565b3480156103e557600080fd5b5061022b611056565b3480156103fa57600080fd5b5061042d6104093660046149e6565b6001600160a01b03166000908152609f60205260409020546001600160401b031690565b60405190815260200161028d565b34801561044757600080fd5b5061046b6104563660046149e6565b60046020526000908152604090205460ff1681565b60405160ff909116815260200161028d565b34801561048957600080fd5b5061022b610498366004614a03565b61111c565b3480156104a957600080fd5b5061022b6104b83660046149b2565b61120d565b61022b6104cb366004614b23565b6112ed565b3480156104dc57600080fd5b5061022b6112fa565b61022b61130e565b3480156104f957600080fd5b5061022b610508366004614b3f565b61142a565b34801561051957600080fd5b5061022b610528366004614b6b565b611439565b34801561053957600080fd5b5061022b610548366004614b3f565b611591565b34801561055957600080fd5b5061056261159c565b60405161028d9190614bc1565b34801561057b57600080fd5b5061022b61058a366004614b6b565b6115ab565b34801561059b57600080fd5b5061022b6105aa366004614c19565b6117db565b3480156105bb57600080fd5b506106216105ca3660046149b2565b60a360205260009081526040902080546001909101546001600160a01b03808316926001600160401b03600160a01b80830482169460ff600160e01b9485900416948116939181049092169162ffffff9190041686565b604080516001600160a01b0397881681526001600160401b0396871660208201529415159085015294909116606083015291909116608082015262ffffff90911660a082015260c00161028d565b34801561067b57600080fd5b50610684611884565b60405161028d9190614c84565b34801561069d57600080fd5b506106b16106ac366004614cd1565b6118e6565b60408051601793840b81529190920b60208201520161028d565b3480156106d757600080fd5b5061022b6106e6366004614d17565b611937565b3480156106f757600080fd5b5060a254610562906001600160a01b031681565b34801561071757600080fd5b5061022b610726366004614d9b565b611c2f565b61022b6107393660046149e6565b611d3a565b34801561074a57600080fd5b5061075e6107593660046149e6565b611d58565b604051901515815260200161028d565b61022b611da3565b34801561078257600080fd5b50610796610791366004614ddc565b611dac565b60405161028d9190614e9f565b3480156107af57600080fd5b506107c36107be366004614eda565b611ea4565b60405160179190910b815260200161028d565b3480156107e257600080fd5b5060055461046b9060ff1681565b3480156107fc57600080fd5b5061022b61080b366004614f13565b611ed4565b34801561081c57600080fd5b5061022b61082b366004614f9a565b611f4a565b34801561083c57600080fd5b5061075e61084b366004614ff6565b611fa2565b34801561085c57600080fd5b5061022b61086b36600461502a565b61201c565b34801561087c57600080fd5b5060055461046b90610100900460ff1681565b34801561089b57600080fd5b5061075e6108aa3660046149b2565b60a46020526000908152604090205460ff1681565b3480156108cb57600080fd5b5061022b6108da3660046149e6565b612051565b3480156108eb57600080fd5b506108ff6108fa3660046149e6565b6120c7565b60405161028d9190615057565b34801561091857600080fd5b5061092c6109273660046150cb565b612173565b604080516001600160a01b0390941684526001600160401b0390921660208401526001600160c01b03169082015260600161028d565b34801561096e57600080fd5b5061098261097d3660046149e6565b6121d2565b60405161028d919061510d565b600080600061099c612296565b8760a001516000036109c1576040516328ebf24760e01b815260040160405180910390fd5b604051632c1affb960e01b8152734a7aa0fcf9b35d87b4125c90484497bb2e050e8590632c1affb990610a049033908c908c908c906001906002906004016151d7565b60006040518083038186803b158015610a1c57600080fd5b505af4158015610a30573d6000803e3d6000fd5b5050604051632da073a160e01b8152734a7aa0fcf9b35d87b4125c90484497bb2e050e859250632da073a19150610a739033908d908d908b908b90600401615221565b606060405180830381865af4158015610a90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab49190615267565b91945092509050610ac56001603955565b96509650969350505050565b6008546001600160a01b03163314610afc576040516387ece76560e01b815260040160405180910390fd5b6000808660000151610140015160ff16600103610b38578651610b2a9087876001600160401b038816611937565b505084516040015182610b75565b610b5186886000015187876001600160401b0316611937565b86516060015191506305f5e100610b6886866152ab565b610b7291906152f0565b90505b6040805160c08101825288516020908101516001600160a01b0390811683526001600160401b0380881683850152908616838501528416606083015289015162ffffff1660808201529088015160a0820152875151610bd490826122ef565b5050505050505050565b604051634a3e552760e11b81527359723329a1e906629516b30ca9c30400b11a1ecd9063947caa4e90610c229086908690869060a49060019060029060040161535b565b60006040518083038186803b158015610c3a57600080fd5b505af4158015610c4e573d6000803e3d6000fd5b50610c64925061075991505060208501856149e6565b610c815760405163188da0b360e21b815260040160405180910390fd5b7f62d93a8b1423235bfd6a4737b2e2069ee7c9ef53c8dab794a345f4d988d3f7e5610caf60208501856149e6565b610cbf60408601602087016149e6565b610ccf60808701606088016149e6565b8585604051610ce2959493929190615467565b60405180910390a1505050565b6008546001600160a01b03163314610d1a576040516387ece76560e01b815260040160405180910390fd5b610d22612296565b6000610d41610d308961549c565b878b6001600160701b031642612441565b9050610d5360a0890160808a016149e6565b6001600160a01b031686602001516001600160a01b031614610d8557610d80610d7b8961549c565b612713565b610db4565b610d96610100890160e08a016149cb565b6001600160401b03168660a001818151610db091906154a8565b9052505b734a7aa0fcf9b35d87b4125c90484497bb2e050e85632c1affb9610ddb60208b018b6149e6565b888888600160026040518763ffffffff1660e01b8152600401610e03969594939291906151d7565b60006040518083038186803b158015610e1b57600080fd5b505af4158015610e2f573d6000803e3d6000fd5b50734a7aa0fcf9b35d87b4125c90484497bb2e050e85925063d5542b6b9150839050610e5e60208c018c6149e6565b8a8a88886040518763ffffffff1660e01b8152600401610e83969594939291906154bf565b60006040518083038186803b158015610e9b57600080fd5b505af4158015610eaf573d6000803e3d6000fd5b50505050610ed9818960a0016020810190610eca91906149cb565b6001600160401b03168b6127b6565b50610bd46001603955565b610eef868686610bde565b610efa838383610bde565b505050505050565b3360008181526001602090815260408083206006546001600160a01b031684529091529020546001600160401b038316601790810b91900b1215610f595760405163569d45cf60e11b815260040160405180910390fd5b6001600160a01b038082166000908152609f6020908152604080832060018352818420600654909516845293909152812080546001600160401b0386169290610fa690849060170b61550c565b82546001600160c01b039182166101009390930a928302919092021990911617905550600654600080516020615f3e8339815191529083906001600160a01b0316610ff96001600160401b03871661555c565b6040516110089392919061557f565b60405180910390a180548390829060009061102d9084906001600160401b03166155a6565b92506101000a8154816001600160401b0302191690836001600160401b03160217905550505050565b336000818152609f602090815260408083208054600184528285206006546001600160a01b03168652909352908320805491936001600160401b039093169290916110a590849060170b6155d1565b82546001600160c01b039182166101009390930a9283029190920219909116179055506006548154604051600080516020615f3e833981519152926111039286926001600160a01b03909216916001600160401b039091169061557f565b60405180910390a180546001600160481b031916905550565b6008546040516321963ec160e01b81526000917359723329a1e906629516b30ca9c30400b11a1ecd916321963ec191611174918a918a918a918a918a916001600160a01b03169060a390600190600290600401615622565b602060405180830381865af4158015611191573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111b59190615682565b60018101546040519192507fe4efc926785e8287f79fae88209e8625600f4493b87d12e6eff70d9eba5bc755916111fd9189916001600160a01b03909116908990899061569b565b60405180910390a1505050505050565b604051632f7f859560e01b81526004810182905260a3602482015260016044820152600260648201526000907359723329a1e906629516b30ca9c30400b11a1ecd90632f7f859590608401602060405180830381865af4158015611275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112999190615682565b805460018201546040519293507faa0d989110241a1a8280e797bfec099d9846f91bf9b168fc911a329ad7fa0228926112e1926001600160a01b0390811692169086906156d2565b60405180910390a15050565b6112f733826122ef565b50565b611302612847565b61130c60006128a6565b565b603a54610100900460ff161580801561132e5750603a54600160ff909116105b8061134f575061133d306128f8565b15801561134f5750603a5460ff166001145b6113b75760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b603a805460ff1916600117905580156113da57603a805461ff0019166101001790555b6113e2612907565b80156112f757603a805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b6114358282336115ab565b5050565b611441612296565b6040516370a0823160e01b81526000906001600160a01b038516906370a0823190611470903090600401614bc1565b602060405180830381865afa15801561148d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114b19190615682565b90506114d16001600160a01b03851633306001600160701b038716612936565b6040516370a0823160e01b815281906001600160a01b038616906370a08231906114ff903090600401614bc1565b602060405180830381865afa15801561151c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115409190615682565b61154a91906154a8565b9050826001600160701b03168110156115765760405163569d45cf60e11b815260040160405180910390fd5b61158184828461298e565b5061158c6001603955565b505050565b611435828233611439565b606d546001600160a01b031690565b6115b3612296565b60006115c884846001600160701b0316612b09565b3360009081526001602090815260408083206001600160a01b0389168452909152812080549293506001600160701b0384169290919061160c90849060170b61550c565b92506101000a8154816001600160c01b03021916908360170b6001600160c01b03160217905550600080516020615f3e8339815191528285836001600160701b03166116579061555c565b6040516116669392919061557f565b60405180910390a13360009081526001602090815260408083206001600160a01b038816845290915281205460170b12156116b45760405163569d45cf60e11b815260040160405180910390fd5b6116bd82611d58565b6116da5760405163188da0b360e21b815260040160405180910390fd5b6001600160a01b03841661176b576000826001600160a01b0316846001600160701b031660405160006040518083038185875af1925050503d806000811461173e576040519150601f19603f3d011682016040523d82523d6000602084013e611743565b606091505b50509050806117655760405163569d45cf60e11b815260040160405180910390fd5b50611788565b6117886001600160a01b038516836001600160701b038616612c06565b836001600160a01b0316826001600160a01b0316336001600160a01b0316600080516020615f5e833981519152600085426040516117c8939291906156f6565b60405180910390a45061158c6001603955565b6117e3612847565b60005b8381101561187d5782828281811061180057611800615720565b90506020020160208101906118159190615736565b6004600087878581811061182b5761182b615720565b905060200201602081019061184091906149e6565b6001600160a01b031681526020810191909152604001600020805460ff191660ff929092169190911790558061187581615753565b9150506117e6565b5050505050565b606060038054806020026020016040519081016040528092919081815260200182805480156118dc57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116118be575b5050505050905090565b60008281526020819052604081205460a083015160e08401516001600160c01b0390921692916001600160401b03918216916119249185911661576c565b61192e919061578b565b90509250929050565b61193f612296565b60006305f5e1006119626001600160401b0385166001600160701b03851661576c565b61196c919061578b565b90506001600160701b03811061199557604051631a93c68960e11b815260040160405180910390fd5b60085460405163c956a3b760e01b81528291600091829173e8c2ac36f9e689f0b4b7c52d80b42fdfbd6645679163c956a3b7916119ec918c918c9133918c918e9142916001600160a01b03909116906004016158dd565b6040805180830381865af4158015611a08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2c9190615950565b91509150611a48828960a001516001600160401b0316876127b6565b611a60818860a001516001600160401b0316876127b6565b611a8b6040518060800160405280600081526020016000815260200160008152602001600081525090565b611a9c898786600360016002612c36565b60408301528152611ab38887866002600181612c36565b6060830152602082015280518951604080840151908c0151611adb9392919060016002612e4a565b611afb8160200151896000015183606001518b6060015160016002612e4a565b8851611b0690611d58565b611b235760405163188da0b360e21b815260040160405180910390fd5b8751611b2e90611d58565b611b4b5760405163188da0b360e21b815260040160405180910390fd5b87600001516001600160a01b031689600001516001600160a01b03167f22aceb00bb2ded7eb0facc722fa061971f5950b739ecb216300e019b74ccd4a08b604001518c606001518b8b8a8a8a604051602001611bb1929190918252602082015260400190565b60408051601f1981840301815282825280516020918201206001600160a01b03988916845296909716968201969096526001600160401b0393909316838601526001600160701b03918216606084015216608082015260a081019190915290519081900360c00190a35050505050611c296001603955565b50505050565b6000805b600354811015611c8d57836001600160a01b031660038281548110611c5a57611c5a615720565b6000918252602090912001546001600160a01b031603611c7d5760019150611c8d565b611c8681615753565b9050611c33565b5080611cac5760405163a87fa00b60e01b815260040160405180910390fd5b6000611cb786613005565b6040516309f70f0560e31b815290915073dc91af7b038cc8760a0c53814f21be09caace0d790634fb8782890611d029060039060029060019060049088908d908d908d9085016159da565b60006040518083038186803b158015611d1a57600080fd5b505af4158015611d2e573d6000803e3d6000fd5b50505050505050505050565b611d42612296565b611d4e6000348361298e565b6112f76001603955565b6001600160a01b0381166000908152600260205260408120548103611d7f57506001919050565b6000611d8a836121d2565b516004811115611d9c57611d9c6150f7565b1492915050565b61130c33611d3a565b606082516001600160401b03811115611dc757611dc76142fb565b604051908082528060200260200182016040528015611df0578160200160208202803683370190505b50905060005b8351811015611e9d576001600160a01b03831660009081526001602052604081208551909190869084908110611e2e57611e2e615720565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a900460170b828281518110611e7657611e76615720565b602002602001019060170b908160170b815250508080611e9590615753565b915050611df6565b5092915050565b6001600160a01b0380821660009081526001602090815260408083209386168352929052205460170b5b92915050565b611edc612847565b611ee860038787614251565b506005805460ff95861661ffff199091161761010094909516939093029390931762010000600160901b031916620100006001600160401b0392831602600160501b600160901b03191617600160501b93909116929092029190911790555050565b611f52612847565b600680546001600160a01b039586166001600160a01b0319918216179091556007805494861694821694909417909355600880549285169284169290921790915560a28054919093169116179055565b60405163e7e652ed60e01b815260009073e8c2ac36f9e689f0b4b7c52d80b42fdfbd6645679063e7e652ed90611fdc908590600401615a3c565b6040805180830381865af4158015611ff8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e9d9190615a5f565b6008546001600160a01b03163314612047576040516387ece76560e01b815260040160405180910390fd5b61143582826122ef565b612059612847565b6001600160a01b0381166120be5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016113ae565b6112f7816128a6565b6001600160a01b0381166000908152600260209081526040808320805482518185028101850190935280835260609492939192909184015b82821015612168576000848152602090819020604080516060810182526002860290920180546001600160a01b0381168452600160a01b90046001600160401b0316838501526001908101546001600160c01b03169183019190915290835290920191016120ff565b505050509050919050565b6002602052816000526040600020818154811061218f57600080fd5b6000918252602090912060029091020180546001909101546001600160a01b0382169350600160a01b9091046001600160401b031691506001600160c01b031683565b6121fe604080516080810190915280600081526020016000815260200160008152602001600081525090565b600061220983613005565b60405163f127baa160e01b815290915073dc91af7b038cc8760a0c53814f21be09caace0d79063f127baa19061224e9060039060029060019060049088908201615a8b565b608060405180830381865af415801561226b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061228f9190615ab6565b9392505050565b6002603954036122e85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016113ae565b6002603955565b6122f7612296565b6040805163c568f68560e01b81526001600160a01b03808516600483015283518116602483015260208401516001600160401b0390811660448401529284015116606482015260608301519091166084820152608082015162ffffff1660a482015260a082015160c482015260a360e4820152600161010482015260026101248201527359723329a1e906629516b30ca9c30400b11a1ecd9063c568f685906101440160006040518083038186803b1580156123b257600080fd5b505af41580156123c6573d6000803e3d6000fd5b505050506123d382611d58565b6123f05760405163188da0b360e21b815260040160405180910390fd5b7f8156ea5248573c664d8d0995ccb941c2cd87e2e6fa12d8a56594295bae8d14bb816000015182604001518360a0015160405161242f939291906156d2565b60405180910390a16114356001603955565b60008061244d866130a8565b92509050806124845760405162461bcd60e51b815260206004820152600360248201526222992160e91b60448201526064016113ae565b60006305f5e1008760c001516001600160401b0316866124a4919061576c565b6124ae919061578b565b905060008088610140015160ff1660010361252b5787600001516001600160a01b031689606001516001600160a01b0316148015612505575087602001516001600160a01b031689604001516001600160a01b0316145b6125215760405162461bcd60e51b81526004016113ae90615b28565b508190508561258f565b87600001516001600160a01b031689604001516001600160a01b031614801561256d575087602001516001600160a01b031689606001516001600160a01b0316145b6125895760405162461bcd60e51b81526004016113ae90615b28565b50859050815b87606001516001600160a01b031689600001516001600160a01b0316146125ec5760405162461bcd60e51b815260206004820152601160248201527024b731b7b93932b1ba2932b1b2b4bb32b960791b60448201526064016113ae565b8760800151821461260f5760405162461bcd60e51b81526004016113ae90615b46565b8760a001518110156126335760405162461bcd60e51b81526004016113ae90615b46565b8860a001516001600160401b03168711156126785760405162461bcd60e51b81526020600482015260056024820152642299a0b6a160d91b60448201526064016113ae565b856103e88a610120015161268c91906152f0565b6001600160401b031610156126c95760405162461bcd60e51b8152602060048201526003602482015262229a2160e91b60448201526064016113ae565b88608001516001600160a01b031688602001516001600160a01b031603612707578860e001516001600160401b03168860a001511161270757600080fd5b50505050949350505050565b612740816000015182608001518360e001516001600160401b031661273790615b6f565b60016002613272565b5080516001600160a01b039081166000908152600160209081526040808320608086015190941683529290529081205460170b121561279257604051631e9acf1760e31b815260040160405180910390fd5b611435816020015182608001518360e001516001600160401b031660016002613272565b6000838152602081905260409020546001600160c01b03166127e16001600160701b03831682615b8b565b9050826001600160c01b0316816001600160c01b03161115612816576040516341a26a6360e01b815260040160405180910390fd5b60009384526020849052604090932080546001600160c01b0319166001600160c01b03909416939093179092555050565b3361285061159c565b6001600160a01b03161461130c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016113ae565b606d80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b03163b151590565b603a54610100900460ff1661292e5760405162461bcd60e51b81526004016113ae90615bad565b61130c613471565b611c29846323b872dd60e01b858585604051602401612957939291906156d2565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526134a1565b6001600160a01b03808216600090815260016020908152604080832093871683529290529081205460170b8113906129cf856001600160701b038616612b09565b6001600160a01b038085166000908152600160209081526040808320938a16835292905290812080549293506001600160701b03841692909190612a1790849060170b6155d1565b92506101000a8154816001600160c01b03021916908360170b6001600160c01b03160217905550600080516020615f3e8339815191528386836001600160701b0316604051612a689392919061557f565b60405180910390a16001600160701b03841615612ac857846001600160a01b0316836001600160a01b0316336001600160a01b0316600080516020615f5e83398151915260018542604051612abf939291906156f6565b60405180910390a45b811561187d576001600160a01b0380841660009081526001602090815260408083209389168352929052205461187d9084908790600290859060170b613576565b6000806001600160a01b038416612b4157612b3a670de0b6b3a7640000612b34856305f5e100613720565b9061372c565b9050612bca565b6000846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba59190615bf8565b60ff169050612bc6612bb882600a615cf9565b612b34866305f5e100613720565b9150505b60016001606f1b03811061228f5760405162461bcd60e51b815260206004820152600360248201526245335560e81b60448201526064016113ae565b6040516001600160a01b03831660248201526044810182905261158c90849063a9059cbb60e01b90606401612957565b60008060018516156002861615801590612c93578960a001516001600160401b0316896001600160701b03168b60e001516001600160401b0316612c7a919061576c565b612c84919061578b565b6001600160401b031660e08b01525b506001600160701b03881615612e3e5760408051608081018252600080825260208201819052918101829052606081019190915281612cf057612ce16001600160701b038916600019615d05565b896001600160701b0316612d10565b612d056001600160701b038a16600019615d05565b886001600160701b03165b6040830152815281612d2b5789606001518a60400151612d36565b89604001518a606001515b6001600160a01b0390811660608401529081166020830181905260808c01516001921603612d85578a60e001516001600160401b031682600001818151612d7d9190615d8a565b905250612df7565b81606001516001600160a01b03168b608001516001600160a01b031603612dc5578a60e001516001600160401b031682604001818151612d7d9190615d8a565b612df48b600001518c608001518d60e001516001600160401b0316600019612ded9190615d05565b8a8a613272565b90505b8a5160208301518351612e0d9291908a8a613272565b8116945081604001519350612e3a8b602001518c608001518d60e001516001600160401b03168a8a613272565b5050505b50965096945050505050565b6001600160a01b0380861660009081526020848152604080832093871683529290529081205460170b90612e7e8287615dc9565b905060008612158015612e915750818112155b80612ea65750600086128015612ea657508181125b612ec25760405162461bcd60e51b81526004016113ae90615e0a565b600088158015612ed957506001600160a01b038616155b8015612ef55750662386f26fc10000886001600160a01b031631105b15612eff57600198505b886001148015612f0f5750600087135b8015612f1b5750600082135b15612fd7576000612f2c8789613738565b6001600160701b0316905060006001600160a01b03881615612fba576040516370a0823160e01b81526001600160a01b038916906370a0823190612f74903090600401614bc1565b602060405180830381865afa158015612f91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fb59190615682565b612fbc565b475b9050818110612fd457612fd0888b846137e9565b8892505b50505b6000612fe38289615d8a565b90508015611d2e57612ff88988838989613272565b5050505050505050505050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152506040805160e0810182526001600160a01b03928316815260075483166020820152600654909216908201526005546001600160401b03600160501b82048116606084015262010000820416608083015260ff80821660a08401526101009091041660c082015290565b60008060006130b6846138ab565b90506000604051602001613121907f454950373132446f6d61696e28737472696e67206e616d652c737472696e672081527f76657273696f6e2c75696e7432353620636861696e49642c627974657333322060208201526473616c742960d81b604082015260450190565b60408051601f1981840301815282825280516020918201208383018352600e84526d4f72696f6e2045786368616e676560901b93820193909352815180830183526001808252603160f81b918301919091528251808301949094527fabed71ea2445a13c99e4e4afa62b708e9aaf6cd13041ba86482e6cbf83fbc4cf848401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606085015260808401527ff2d857f4a3edcb9b78b4d503bfe733db1e3f6cdc2b7971ee739626c97e86a55760a0808501919091528251808503909101815260c08401835280519082012061190160f01b60e085015260e284015261010280840186905282518085039091018152610122909301909152815191012085516101608701519192506001600160a01b03169061325d908390613a39565b6001600160a01b031614959194509092505050565b6001600160a01b0380861660009081526020848152604080832093881683529290529081205460170b816132a68683615dc9565b9050600086121580156132b95750818112155b806132ce57506000861280156132ce57508181125b6132ea5760405162461bcd60e51b81526004016113ae90615e0a565b6000861380156132fa5750600082125b156133115761330c8888868985613576565b613386565b600082121580156133225750600081125b15613386576001600160a01b038716156133595761334c8761334683600019615d05565b8a613a5d565b6133569082615dc9565b90505b600081121561336e5761330c88888387613b87565b6000821361337d576001613380565b60005b60ff1692505b808214613466576001600160bf1b031981128015906133ac57506001600160bf1b038113155b6133c85760405162461bcd60e51b81526004016113ae90615e0a565b6001600160a01b03808916600090815260208781526040808320938b168352929052908120546133fb9060170b8361550c565b6001600160a01b038a8116600090815260208981526040808320938d16835292905281902080546001600160c01b0319166001600160c01b03861617905551909150600080516020615f3e8339815191529061345c908b908b90859061557f565b60405180910390a1505b505095945050505050565b603a54610100900460ff166134985760405162461bcd60e51b81526004016113ae90615bad565b61130c336128a6565b60006134f6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613c639092919063ffffffff16565b90508051600014806135175750808060200190518101906135179190615e27565b61158c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016113ae565b60008160170b126135915761358c858585613c7a565b61187d565b6001600160a01b0385166000908152602084905260408120545b6135b66001826154a8565b82101561361d576001600160a01b038781166000908152602087905260409020805491881691849081106135ec576135ec615720565b60009182526020909120600290910201546001600160a01b03161461361d578161361581615753565b9250506135ab565b6001600160a01b038716600090815260208690526040812080548490811061364757613647615720565b6000918252602090912060029091020160018101549091506001600160c01b03166001600160701b038616106136c9576136808461555c565b6001820180546001600160c01b0319166001600160c01b03929092169190911790558054600160a01b600160e01b031916600160a01b426001600160401b031602178155610bd4565b6001810180546001600160701b03871691906000906136f29084906001600160c01b0316615e42565b92506101000a8154816001600160c01b0302191690836001600160c01b031602179055505050505050505050565b600061228f828461576c565b600061228f828461578b565b6000806001600160a01b03841661376357612b3a6305f5e100612b3485670de0b6b3a7640000613720565b6000846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137c79190615bf8565b60ff169050612bc66305f5e100612b346137e284600a615cf9565b8790613720565b801561158c576137f883613e5d565b15613897578047101561381e57604051631e9acf1760e31b815260040160405180910390fd5b6000826001600160a01b03168261138890604051600060405180830381858888f193505050503d8060008114613870576040519150601f19603f3d011682016040523d82523d6000602084013e613875565b606091505b5050905080611c295760405163b12d13eb60e01b815260040160405180910390fd5b61158c6001600160a01b0384168383612c06565b60006040516020016139b1907f4f7264657228616464726573732073656e646572416464726573732c6164647281527f657373206d617463686572416464726573732c6164647265737320626173654160208201527f737365742c616464726573732071756f746541737365742c616464726573732060408201527f6d61746368657246656541737365742c75696e74363420616d6f756e742c756960608201527f6e7436342070726963652c75696e743634206d6174636865724665652c75696e60808201527f743634206e6f6e63652c75696e7436342065787069726174696f6e2c75696e7460a0820152693820627579536964652960b01b60c082015260ca0190565b60405160208183030381529060405280519060200120826000015183602001518460400151856060015186608001518760a001518860c001518960e001518a61010001518b61012001518c6101400151604051602001613a1c9c9b9a99989796959493929190615e6a565b604051602081830303815290604052805190602001209050919050565b6000806000613a488585613e96565b91509150613a5581613edb565b509392505050565b600080613a6a8585613738565b6001600160701b0316905080856001600160a01b03166370a08231856040518263ffffffff1660e01b8152600401613aa29190614bc1565b602060405180830381865afa158015613abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ae39190615682565b10158015613b625750604051636eb1769f60e11b81526001600160a01b03848116600483015230602483015282919087169063dd62ed3e90604401602060405180830381865afa158015613b3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b5f9190615682565b10155b15613b7c57613b7385843084612936565b8391505061228f565b506000949350505050565b806000856001600160a01b03166001600160a01b031681526020019081526020016000206040518060600160405280856001600160a01b03168152602001426001600160401b0316815260200184613bde9061555c565b6001600160c01b03908116909152825460018181018555600094855260209485902084516002909302018054958501516001600160401b0316600160a01b026001600160e01b03199096166001600160a01b0390931692909217949094178155604090920151919092018054919092166001600160c01b031990911617905550505050565b6060613c728484600085614020565b949350505050565b6001600160a01b038316600090815260208290526040812054905b8181101561187d576001600160a01b03858116600090815260208590526040902080549186169183908110613ccc57613ccc615720565b60009182526020909120600290910201546001600160a01b031603613e4b576001821115613de9576001600160a01b0385166000908152602084905260409020613d176001846154a8565b81548110613d2757613d27615720565b9060005260206000209060020201836000876001600160a01b03166001600160a01b031681526020019081526020016000208281548110613d6a57613d6a615720565b60009182526020909120825460029092020180546001600160a01b031981166001600160a01b03909316928317825583546001600160401b03600160a01b9182900416026001600160e01b0319909116909217919091178155600191820154910180546001600160c01b0319166001600160c01b039092169190911790555b6001600160a01b0385166000908152602084905260409020805480613e1057613e10615ef8565b60008281526020902060026000199092019182020180546001600160e01b031916815560010180546001600160c01b0319169055905561187d565b80613e5581615753565b915050613c95565b60006001600160a01b0382161580611ece57506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1492915050565b6000808251604103613ecc5760208301516040840151606085015160001a613ec0878285856140fb565b94509450505050613ed4565b506000905060025b9250929050565b6000816004811115613eef57613eef6150f7565b03613ef75750565b6001816004811115613f0b57613f0b6150f7565b03613f535760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b60448201526064016113ae565b6002816004811115613f6757613f676150f7565b03613fb45760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016113ae565b6003816004811115613fc857613fc86150f7565b036112f75760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016113ae565b6060824710156140815760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016113ae565b600080866001600160a01b0316858760405161409d9190615f0e565b60006040518083038185875af1925050503d80600081146140da576040519150601f19603f3d011682016040523d82523d6000602084013e6140df565b606091505b50915091506140f0878383876141b5565b979650505050505050565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b0383111561412857506000905060036141ac565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561417c573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166141a5576000600192509250506141ac565b9150600090505b94509492505050565b6060831561422257825160000361421b576141cf856128f8565b61421b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016113ae565b5081613c72565b613c7283838151156142375781518083602001fd5b8060405162461bcd60e51b81526004016113ae9190615f2a565b8280548282559060005260206000209081019282156142a4579160200282015b828111156142a45781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614271565b506142b09291506142b4565b5090565b5b808211156142b057600081556001016142b5565b6001600160401b03169052565b6001600160a01b03811681146112f757600080fd5b80356142f6816142d6565b919050565b634e487b7160e01b600052604160045260246000fd5b60405161018081016001600160401b0381118282101715614334576143346142fb565b60405290565b604051606081016001600160401b0381118282101715614334576143346142fb565b604051601f8201601f191681016001600160401b0381118282101715614384576143846142fb565b604052919050565b600060e0828403121561439e57600080fd5b60405160e081016001600160401b03811182821017156143c0576143c06142fb565b60405290508082356143d1816142d6565b815260208301356143e1816142d6565b602082015260408301356143f4816142d6565b60408201526060830135614407816142d6565b806060830152506080830135608082015260a083013560a082015260c083013560c08201525092915050565b60008083601f84011261444557600080fd5b5081356001600160401b0381111561445c57600080fd5b602083019150836020828501011115613ed457600080fd5b600080600080600080610140878903121561448e57600080fd5b8635614499816142d6565b95506144a8886020890161438c565b94506101008701356001600160401b03808211156144c557600080fd5b6144d18a838b01614433565b90965094506101208901359150808211156144eb57600080fd5b506144f889828a01614433565b979a9699509497509295939492505050565b80356001600160401b03811681146142f657600080fd5b60ff811681146112f757600080fd5b80356142f681614521565b600082601f83011261454c57600080fd5b81356001600160401b03811115614565576145656142fb565b614578601f8201601f191660200161435c565b81815284602083860101111561458d57600080fd5b816020850160208301376000918101602001919091529392505050565b600061018082840312156145bd57600080fd5b6145c5614311565b90506145d0826142eb565b81526145de602083016142eb565b60208201526145ef604083016142eb565b6040820152614600606083016142eb565b6060820152614611608083016142eb565b608082015261462260a0830161450a565b60a082015261463360c0830161450a565b60c082015261464460e0830161450a565b60e082015261010061465781840161450a565b9082015261012061466983820161450a565b9082015261014061467b838201614530565b90820152610160828101356001600160401b0381111561469a57600080fd5b6146a68582860161453b565b82840152505092915050565b803562ffffff811681146142f657600080fd5b600080600080600060a086880312156146dd57600080fd5b85356001600160401b03808211156146f457600080fd5b908701906060828a03121561470857600080fd5b61471061433a565b82358281111561471f57600080fd5b61472b8b8286016145aa565b82525061473a602084016146b2565b60208201526040830135604082015280975050602088013591508082111561476157600080fd5b5061476e888289016145aa565b94505061477d6040870161450a565b925061478b6060870161450a565b91506147996080870161450a565b90509295509295909350565b600061010082840312156147b857600080fd5b50919050565b6000806000604084860312156147d357600080fd5b83356001600160401b03808211156147ea57600080fd5b6147f6878388016147a5565b9450602086013591508082111561480c57600080fd5b5061481986828701614433565b9497909650939450505050565b80356001600160701b03811681146142f657600080fd5b600080600080600080600080610180808a8c03121561485b57600080fd5b6148648a614826565b985060208a01356001600160401b038082111561488057600080fd5b818c01915082828e03121561489457600080fd5b81995060408c013592506148a7836142d6565b8298506148b78d60608e0161438c565b97506101408c01359250808311156148ce57600080fd5b6148da8d848e01614433565b90975095506101608c01359250869150808311156148f757600080fd5b50506149058b828c01614433565b999c989b5096995094979396929594505050565b6000806000806000806080878903121561493257600080fd5b86356001600160401b038082111561494957600080fd5b6149558a838b016147a5565b9750602089013591508082111561496b57600080fd5b6149778a838b01614433565b9097509550604089013591508082111561499057600080fd5b61499c8a838b016147a5565b945060608901359150808211156144eb57600080fd5b6000602082840312156149c457600080fd5b5035919050565b6000602082840312156149dd57600080fd5b61228f8261450a565b6000602082840312156149f857600080fd5b813561228f816142d6565b600080600080600060608688031215614a1b57600080fd5b8535614a26816142d6565b945060208601356001600160401b0380821115614a4257600080fd5b614a4e89838a01614433565b90965094506040880135915080821115614a6757600080fd5b50614a7488828901614433565b969995985093965092949392505050565b600060c08284031215614a9757600080fd5b60405160c081016001600160401b0381118282101715614ab957614ab96142fb565b6040529050808235614aca816142d6565b8152614ad86020840161450a565b60208201526040830135614aeb816142d6565b6040820152614afc6060840161450a565b6060820152614b0d608084016146b2565b608082015260a083013560a08201525092915050565b600060c08284031215614b3557600080fd5b61228f8383614a85565b60008060408385031215614b5257600080fd5b8235614b5d816142d6565b915061192e60208401614826565b600080600060608486031215614b8057600080fd5b8335614b8b816142d6565b9250614b9960208501614826565b91506040840135614ba9816142d6565b809150509250925092565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b60008083601f840112614be757600080fd5b5081356001600160401b03811115614bfe57600080fd5b6020830191508360208260051b8501011115613ed457600080fd5b60008060008060408587031215614c2f57600080fd5b84356001600160401b0380821115614c4657600080fd5b614c5288838901614bd5565b90965094506020870135915080821115614c6b57600080fd5b50614c7887828801614bd5565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015614cc55783516001600160a01b031683529284019291840191600101614ca0565b50909695505050505050565b60008060408385031215614ce457600080fd5b8235915060208301356001600160401b03811115614d0157600080fd5b614d0d858286016145aa565b9150509250929050565b60008060008060808587031215614d2d57600080fd5b84356001600160401b0380821115614d4457600080fd5b614d50888389016145aa565b95506020870135915080821115614d6657600080fd5b50614d73878288016145aa565b935050614d826040860161450a565b9150614d9060608601614826565b905092959194509250565b60008060008060808587031215614db157600080fd5b8435614dbc816142d6565b93506020850135614dcc816142d6565b92506040850135614d82816142d6565b60008060408385031215614def57600080fd5b82356001600160401b0380821115614e0657600080fd5b818501915085601f830112614e1a57600080fd5b8135602082821115614e2e57614e2e6142fb565b8160051b9250614e3f81840161435c565b8281529284018101928181019089851115614e5957600080fd5b948201945b84861015614e835785359350614e73846142d6565b8382529482019490820190614e5e565b9650614e9290508782016142eb565b9450505050509250929050565b6020808252825182820181905260009190848201906040850190845b81811015614cc557835160170b83529284019291840191600101614ebb565b60008060408385031215614eed57600080fd5b8235614ef8816142d6565b91506020830135614f08816142d6565b809150509250929050565b60008060008060008060a08789031215614f2c57600080fd5b86356001600160401b03811115614f4257600080fd5b614f4e89828a01614bd5565b9097509550506020870135614f6281614521565b93506040870135614f7281614521565b9250614f806060880161450a565b9150614f8e6080880161450a565b90509295509295509295565b60008060008060808587031215614fb057600080fd5b8435614fbb816142d6565b93506020850135614fcb816142d6565b92506040850135614fdb816142d6565b91506060850135614feb816142d6565b939692955090935050565b60006020828403121561500857600080fd5b81356001600160401b0381111561501e57600080fd5b613c72848285016145aa565b60008060e0838503121561503d57600080fd5b8235615048816142d6565b915061192e8460208501614a85565b602080825282518282018190526000919060409081850190868401855b828110156150be57815180516001600160a01b03168552868101516001600160401b0316878601528501516001600160c01b03168585015260609093019290850190600101615074565b5091979650505050505050565b600080604083850312156150de57600080fd5b82356150e9816142d6565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b815160808201906005811061513257634e487b7160e01b600052602160045260246000fd5b8083525060208301516020830152604083015160408301526060830151606083015292915050565b60018060a01b03808251168352806020830151166020840152806040830151166040840152806060830151166060840152506080810151608083015260a081015160a083015260c081015160c08301525050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b038716815260006101606151f5602084018961515a565b8061010084015261520981840187896151ae565b61012084019590955250506101400152949350505050565b6001600160a01b038681168252851660208201526000610140615247604084018761515a565b8061012084015261525b81840185876151ae565b98975050505050505050565b60008060006060848603121561527c57600080fd5b8351925060208401519150604084015190509250925092565b634e487b7160e01b600052601160045260246000fd5b60006001600160401b03828116848216811515828404821116156152d1576152d1615295565b02949350505050565b634e487b7160e01b600052601260045260246000fd5b60006001600160401b038381168061530a5761530a6152da565b92169190910492915050565b6000808335601e1984360301811261532d57600080fd5b83016020810192503590506001600160401b0381111561534c57600080fd5b803603821315613ed457600080fd5b60a081526000873561536c816142d6565b6001600160a01b031660a0830152615386602089016142eb565b61539360c0840182614bb4565b506153a0604089016142eb565b6153ad60e0840182614bb4565b506153ba606089016142eb565b6101006153c981850183614bb4565b6153d560808b0161450a565b91506153e56101208501836142c9565b6153f160a08b0161450a565b91506154016101408501836142c9565b60c08a013561016085015261541960e08b018b615316565b9250816101808601526154316101a0860184836151ae565b92505050828103602084015261544881888a6151ae565b6040840196909652505060608101929092526080909101529392505050565b6001600160a01b0386811682528581166020830152841660408201526080606082018190526000906140f090830184866151ae565b6000611ece36836145aa565b6000828210156154ba576154ba615295565b500390565b8681526001600160a01b0386811660208301528516604082015260006101606154eb606084018761515a565b806101408401526154ff81840185876151ae565b9998505050505050505050565b6000601782810b9084900b82811280156001600160bf1b031983018412161561553757615537615295565b6001600160bf1b038201831381161561555257615552615295565b5090039392505050565b6000601782900b600160bf1b810161557657615576615295565b60000392915050565b6001600160a01b03938416815291909216602082015260179190910b604082015260600190565b60006001600160401b038281168482168083038211156155c8576155c8615295565b01949350505050565b6000601782810b9084900b82821280156001600160bf1b03849003831316156155fc576155fc615295565b6001600160bf1b0319839003821281161561561957615619615295565b50019392505050565b600060018060a01b03808c16835260e0602084015261564560e084018b8d6151ae565b8381036040850152615658818a8c6151ae565b9790911660608401525050608081019390935260a083019190915260c09091015295945050505050565b60006020828403121561569457600080fd5b5051919050565b6001600160a01b038581168252841660208201526060604082018190526000906156c890830184866151ae565b9695505050505050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b92151583526001600160701b039190911660208301526001600160401b0316604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561574857600080fd5b813561228f81614521565b60006001820161576557615765615295565b5060010190565b600081600019048311821515161561578657615786615295565b500290565b60008261579a5761579a6152da565b500490565b60005b838110156157ba5781810151838201526020016157a2565b83811115611c295750506000910152565b600081518084526157e381602086016020860161579f565b601f01601f19169290920160200192915050565b6000610180615807848451614bb4565b60208301516158196020860182614bb4565b50604083015161582c6040860182614bb4565b50606083015161583f6060860182614bb4565b5060808301516158526080860182614bb4565b5060a083015161586560a08601826142c9565b5060c083015161587860c08601826142c9565b5060e083015161588b60e08601826142c9565b506101008084015161589f828701826142c9565b5050610120808401516158b4828701826142c9565b50506101408381015160ff1690850152610160808401518186018390526156c8838701826157cb565b60e0815260006158f060e083018a6157f7565b8281036020840152615902818a6157f7565b6001600160a01b0398891660408501526001600160701b0397909716606084015250506001600160401b0393909316608084015260a083019190915290921660c09092019190915292915050565b6000806040838503121561596357600080fd5b505080516020909101519092909150565b60018060a01b0380825116835280602083015116602084015280604083015116604084015250606081015160018060401b038082166060850152806080840151166080850152505060ff60a08201511660a083015260ff60c08201511660c08301525050565b60006101c082019050898252886020830152876040830152866060830152615a056080830187615974565b6001600160a01b03948516610160830152929093166101808401526001600160701b03166101a09092019190915295945050505050565b60208152600061228f60208301846157f7565b805180151581146142f657600080fd5b60008060408385031215615a7257600080fd5b615a7b83615a4f565b9150602083015190509250929050565b6000610160820190508682528560208301528460408301528360608301526156c86080830184615974565b600060808284031215615ac857600080fd5b604051608081016001600160401b0381118282101715615aea57615aea6142fb565b604052825160058110615afc57600080fd5b808252506020830151602082015260408301516040820152606083015160608201528091505092915050565b6020808252600490820152634533417360e01b604082015260600190565b6020808252600f908201526e125b98dbdc9c9958dd105b5bdd5b9d608a1b604082015260600190565b6000600160ff1b8201615b8457615b84615295565b5060000390565b60006001600160c01b038281168482168083038211156155c8576155c8615295565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b600060208284031215615c0a57600080fd5b815161228f81614521565b600181815b80851115615c50578160001904821115615c3657615c36615295565b80851615615c4357918102915b93841c9390800290615c1a565b509250929050565b600082615c6757506001611ece565b81615c7457506000611ece565b8160018114615c8a5760028114615c9457615cb0565b6001915050611ece565b60ff841115615ca557615ca5615295565b50506001821b611ece565b5060208310610133831016604e8410600b8410161715615cd3575081810a611ece565b615cdd8383615c15565b8060001904821115615cf157615cf1615295565b029392505050565b600061228f8383615c58565b60006001600160ff1b0381841382841380821686840486111615615d2b57615d2b615295565b600160ff1b6000871282811687830589121615615d4a57615d4a615295565b60008712925087820587128484161615615d6657615d66615295565b87850587128184161615615d7c57615d7c615295565b505050929093029392505050565b60008083128015600160ff1b850184121615615da857615da8615295565b6001600160ff1b0384018313811615615dc357615dc3615295565b50500390565b600080821280156001600160ff1b0384900385131615615deb57615deb615295565b600160ff1b8390038412811615615e0457615e04615295565b50500190565b60208082526003908201526245313160e81b604082015260600190565b600060208284031215615e3957600080fd5b61228f82615a4f565b60006001600160c01b0383811690831681811015615e6257615e62615295565b039392505050565b8c81526001600160a01b038c811660208301528b811660408301528a811660608301528981166080830152881660a08201526001600160401b0387811660c083015286811660e083015285166101008201526101808101615ecf6101208301866142c9565b615edd6101408301856142c9565b60ff83166101608301529d9c50505050505050505050505050565b634e487b7160e01b600052603160045260246000fd5b60008251615f2081846020870161579f565b9190910192915050565b60208152600061228f60208301846157cb56fe2210a3e9136294756a7a989c32de6a75280c32dafeccc1280adcc9bb469f44a3565d6850cdd88f91fbcedecc61c18940ca739db2f1a27fc66ac9d4b236db81b8a26469706673582212202a3e234e5fdafae8c139081886d76210ecc5dd17e3d18f6f58b16b1039c5524764736f6c634300080f0033

Deployed Bytecode

0x6080604052600436106102065760003560e01c806311fbc3091461025357806312aa3caf1461029657806315ba55e5146102c457806318044e80146102e4578063253d73a81461030457806328d0a326146103245780632b3a54411461034b57806340f1a34d1461036b5780634e91d2c6146103b9578063548dd437146103d957806355664d37146103ee5780636241d7681461043b57806362a3f4dd1461047d5780636628b4641461049d5780636c3175bb146104bd578063715018a6146104d05780638129fc1c146104e55780638293e9af146104ed5780638795e1bb1461050d57806388a0ec621461052d5780638da5cb5b1461054d578063963ad20c1461056f578063a02fbb781461058f578063a0a90856146105af578063a1ff9bee1461066f578063a5ffc19a14610691578063a9d5ec76146106cb578063ad5c4648146106eb578063b0d3b87e1461070b578063b760faf91461072b578063c18c9d931461073e578063d0e30db01461076e578063d1c0702814610776578063d4fac45d146107a3578063dcffd7cb146107d6578063e0c3ebcf146107f0578063e326dbbf14610810578063e4fd7d0114610830578063e658defb14610850578063e6b8b82514610870578063ef74e5941461088f578063f2fde38b146108bf578063f721599e146108df578063f7522af61461090c578063ff782d9b146109625761022d565b3661022d5732330361022b57604051631b10b0f960e01b815260040160405180910390fd5b005b34801561023957600080fd5b506040516314ad190f60e21b815260040160405180910390fd5b34801561025f57600080fd5b50600554610279906201000090046001600160401b031681565b6040516001600160401b0390911681526020015b60405180910390f35b6102a96102a4366004614474565b61098f565b6040805193845260208401929092529082015260600161028d565b3480156102d057600080fd5b5061022b6102df3660046146c5565b610ad1565b3480156102f057600080fd5b5061022b6102ff3660046147be565b610bde565b34801561031057600080fd5b5061022b61031f36600461483d565b610cef565b34801561033057600080fd5b5060055461027990600160501b90046001600160401b031681565b34801561035757600080fd5b5061022b610366366004614919565b610ee4565b34801561037757600080fd5b506103a16103863660046149b2565b6000602081905290815260409020546001600160c01b031681565b6040516001600160c01b03909116815260200161028d565b3480156103c557600080fd5b5061022b6103d43660046149cb565b610f02565b3480156103e557600080fd5b5061022b611056565b3480156103fa57600080fd5b5061042d6104093660046149e6565b6001600160a01b03166000908152609f60205260409020546001600160401b031690565b60405190815260200161028d565b34801561044757600080fd5b5061046b6104563660046149e6565b60046020526000908152604090205460ff1681565b60405160ff909116815260200161028d565b34801561048957600080fd5b5061022b610498366004614a03565b61111c565b3480156104a957600080fd5b5061022b6104b83660046149b2565b61120d565b61022b6104cb366004614b23565b6112ed565b3480156104dc57600080fd5b5061022b6112fa565b61022b61130e565b3480156104f957600080fd5b5061022b610508366004614b3f565b61142a565b34801561051957600080fd5b5061022b610528366004614b6b565b611439565b34801561053957600080fd5b5061022b610548366004614b3f565b611591565b34801561055957600080fd5b5061056261159c565b60405161028d9190614bc1565b34801561057b57600080fd5b5061022b61058a366004614b6b565b6115ab565b34801561059b57600080fd5b5061022b6105aa366004614c19565b6117db565b3480156105bb57600080fd5b506106216105ca3660046149b2565b60a360205260009081526040902080546001909101546001600160a01b03808316926001600160401b03600160a01b80830482169460ff600160e01b9485900416948116939181049092169162ffffff9190041686565b604080516001600160a01b0397881681526001600160401b0396871660208201529415159085015294909116606083015291909116608082015262ffffff90911660a082015260c00161028d565b34801561067b57600080fd5b50610684611884565b60405161028d9190614c84565b34801561069d57600080fd5b506106b16106ac366004614cd1565b6118e6565b60408051601793840b81529190920b60208201520161028d565b3480156106d757600080fd5b5061022b6106e6366004614d17565b611937565b3480156106f757600080fd5b5060a254610562906001600160a01b031681565b34801561071757600080fd5b5061022b610726366004614d9b565b611c2f565b61022b6107393660046149e6565b611d3a565b34801561074a57600080fd5b5061075e6107593660046149e6565b611d58565b604051901515815260200161028d565b61022b611da3565b34801561078257600080fd5b50610796610791366004614ddc565b611dac565b60405161028d9190614e9f565b3480156107af57600080fd5b506107c36107be366004614eda565b611ea4565b60405160179190910b815260200161028d565b3480156107e257600080fd5b5060055461046b9060ff1681565b3480156107fc57600080fd5b5061022b61080b366004614f13565b611ed4565b34801561081c57600080fd5b5061022b61082b366004614f9a565b611f4a565b34801561083c57600080fd5b5061075e61084b366004614ff6565b611fa2565b34801561085c57600080fd5b5061022b61086b36600461502a565b61201c565b34801561087c57600080fd5b5060055461046b90610100900460ff1681565b34801561089b57600080fd5b5061075e6108aa3660046149b2565b60a46020526000908152604090205460ff1681565b3480156108cb57600080fd5b5061022b6108da3660046149e6565b612051565b3480156108eb57600080fd5b506108ff6108fa3660046149e6565b6120c7565b60405161028d9190615057565b34801561091857600080fd5b5061092c6109273660046150cb565b612173565b604080516001600160a01b0390941684526001600160401b0390921660208401526001600160c01b03169082015260600161028d565b34801561096e57600080fd5b5061098261097d3660046149e6565b6121d2565b60405161028d919061510d565b600080600061099c612296565b8760a001516000036109c1576040516328ebf24760e01b815260040160405180910390fd5b604051632c1affb960e01b8152734a7aa0fcf9b35d87b4125c90484497bb2e050e8590632c1affb990610a049033908c908c908c906001906002906004016151d7565b60006040518083038186803b158015610a1c57600080fd5b505af4158015610a30573d6000803e3d6000fd5b5050604051632da073a160e01b8152734a7aa0fcf9b35d87b4125c90484497bb2e050e859250632da073a19150610a739033908d908d908b908b90600401615221565b606060405180830381865af4158015610a90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab49190615267565b91945092509050610ac56001603955565b96509650969350505050565b6008546001600160a01b03163314610afc576040516387ece76560e01b815260040160405180910390fd5b6000808660000151610140015160ff16600103610b38578651610b2a9087876001600160401b038816611937565b505084516040015182610b75565b610b5186886000015187876001600160401b0316611937565b86516060015191506305f5e100610b6886866152ab565b610b7291906152f0565b90505b6040805160c08101825288516020908101516001600160a01b0390811683526001600160401b0380881683850152908616838501528416606083015289015162ffffff1660808201529088015160a0820152875151610bd490826122ef565b5050505050505050565b604051634a3e552760e11b81527359723329a1e906629516b30ca9c30400b11a1ecd9063947caa4e90610c229086908690869060a49060019060029060040161535b565b60006040518083038186803b158015610c3a57600080fd5b505af4158015610c4e573d6000803e3d6000fd5b50610c64925061075991505060208501856149e6565b610c815760405163188da0b360e21b815260040160405180910390fd5b7f62d93a8b1423235bfd6a4737b2e2069ee7c9ef53c8dab794a345f4d988d3f7e5610caf60208501856149e6565b610cbf60408601602087016149e6565b610ccf60808701606088016149e6565b8585604051610ce2959493929190615467565b60405180910390a1505050565b6008546001600160a01b03163314610d1a576040516387ece76560e01b815260040160405180910390fd5b610d22612296565b6000610d41610d308961549c565b878b6001600160701b031642612441565b9050610d5360a0890160808a016149e6565b6001600160a01b031686602001516001600160a01b031614610d8557610d80610d7b8961549c565b612713565b610db4565b610d96610100890160e08a016149cb565b6001600160401b03168660a001818151610db091906154a8565b9052505b734a7aa0fcf9b35d87b4125c90484497bb2e050e85632c1affb9610ddb60208b018b6149e6565b888888600160026040518763ffffffff1660e01b8152600401610e03969594939291906151d7565b60006040518083038186803b158015610e1b57600080fd5b505af4158015610e2f573d6000803e3d6000fd5b50734a7aa0fcf9b35d87b4125c90484497bb2e050e85925063d5542b6b9150839050610e5e60208c018c6149e6565b8a8a88886040518763ffffffff1660e01b8152600401610e83969594939291906154bf565b60006040518083038186803b158015610e9b57600080fd5b505af4158015610eaf573d6000803e3d6000fd5b50505050610ed9818960a0016020810190610eca91906149cb565b6001600160401b03168b6127b6565b50610bd46001603955565b610eef868686610bde565b610efa838383610bde565b505050505050565b3360008181526001602090815260408083206006546001600160a01b031684529091529020546001600160401b038316601790810b91900b1215610f595760405163569d45cf60e11b815260040160405180910390fd5b6001600160a01b038082166000908152609f6020908152604080832060018352818420600654909516845293909152812080546001600160401b0386169290610fa690849060170b61550c565b82546001600160c01b039182166101009390930a928302919092021990911617905550600654600080516020615f3e8339815191529083906001600160a01b0316610ff96001600160401b03871661555c565b6040516110089392919061557f565b60405180910390a180548390829060009061102d9084906001600160401b03166155a6565b92506101000a8154816001600160401b0302191690836001600160401b03160217905550505050565b336000818152609f602090815260408083208054600184528285206006546001600160a01b03168652909352908320805491936001600160401b039093169290916110a590849060170b6155d1565b82546001600160c01b039182166101009390930a9283029190920219909116179055506006548154604051600080516020615f3e833981519152926111039286926001600160a01b03909216916001600160401b039091169061557f565b60405180910390a180546001600160481b031916905550565b6008546040516321963ec160e01b81526000917359723329a1e906629516b30ca9c30400b11a1ecd916321963ec191611174918a918a918a918a918a916001600160a01b03169060a390600190600290600401615622565b602060405180830381865af4158015611191573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111b59190615682565b60018101546040519192507fe4efc926785e8287f79fae88209e8625600f4493b87d12e6eff70d9eba5bc755916111fd9189916001600160a01b03909116908990899061569b565b60405180910390a1505050505050565b604051632f7f859560e01b81526004810182905260a3602482015260016044820152600260648201526000907359723329a1e906629516b30ca9c30400b11a1ecd90632f7f859590608401602060405180830381865af4158015611275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112999190615682565b805460018201546040519293507faa0d989110241a1a8280e797bfec099d9846f91bf9b168fc911a329ad7fa0228926112e1926001600160a01b0390811692169086906156d2565b60405180910390a15050565b6112f733826122ef565b50565b611302612847565b61130c60006128a6565b565b603a54610100900460ff161580801561132e5750603a54600160ff909116105b8061134f575061133d306128f8565b15801561134f5750603a5460ff166001145b6113b75760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b603a805460ff1916600117905580156113da57603a805461ff0019166101001790555b6113e2612907565b80156112f757603a805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b6114358282336115ab565b5050565b611441612296565b6040516370a0823160e01b81526000906001600160a01b038516906370a0823190611470903090600401614bc1565b602060405180830381865afa15801561148d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114b19190615682565b90506114d16001600160a01b03851633306001600160701b038716612936565b6040516370a0823160e01b815281906001600160a01b038616906370a08231906114ff903090600401614bc1565b602060405180830381865afa15801561151c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115409190615682565b61154a91906154a8565b9050826001600160701b03168110156115765760405163569d45cf60e11b815260040160405180910390fd5b61158184828461298e565b5061158c6001603955565b505050565b611435828233611439565b606d546001600160a01b031690565b6115b3612296565b60006115c884846001600160701b0316612b09565b3360009081526001602090815260408083206001600160a01b0389168452909152812080549293506001600160701b0384169290919061160c90849060170b61550c565b92506101000a8154816001600160c01b03021916908360170b6001600160c01b03160217905550600080516020615f3e8339815191528285836001600160701b03166116579061555c565b6040516116669392919061557f565b60405180910390a13360009081526001602090815260408083206001600160a01b038816845290915281205460170b12156116b45760405163569d45cf60e11b815260040160405180910390fd5b6116bd82611d58565b6116da5760405163188da0b360e21b815260040160405180910390fd5b6001600160a01b03841661176b576000826001600160a01b0316846001600160701b031660405160006040518083038185875af1925050503d806000811461173e576040519150601f19603f3d011682016040523d82523d6000602084013e611743565b606091505b50509050806117655760405163569d45cf60e11b815260040160405180910390fd5b50611788565b6117886001600160a01b038516836001600160701b038616612c06565b836001600160a01b0316826001600160a01b0316336001600160a01b0316600080516020615f5e833981519152600085426040516117c8939291906156f6565b60405180910390a45061158c6001603955565b6117e3612847565b60005b8381101561187d5782828281811061180057611800615720565b90506020020160208101906118159190615736565b6004600087878581811061182b5761182b615720565b905060200201602081019061184091906149e6565b6001600160a01b031681526020810191909152604001600020805460ff191660ff929092169190911790558061187581615753565b9150506117e6565b5050505050565b606060038054806020026020016040519081016040528092919081815260200182805480156118dc57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116118be575b5050505050905090565b60008281526020819052604081205460a083015160e08401516001600160c01b0390921692916001600160401b03918216916119249185911661576c565b61192e919061578b565b90509250929050565b61193f612296565b60006305f5e1006119626001600160401b0385166001600160701b03851661576c565b61196c919061578b565b90506001600160701b03811061199557604051631a93c68960e11b815260040160405180910390fd5b60085460405163c956a3b760e01b81528291600091829173e8c2ac36f9e689f0b4b7c52d80b42fdfbd6645679163c956a3b7916119ec918c918c9133918c918e9142916001600160a01b03909116906004016158dd565b6040805180830381865af4158015611a08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2c9190615950565b91509150611a48828960a001516001600160401b0316876127b6565b611a60818860a001516001600160401b0316876127b6565b611a8b6040518060800160405280600081526020016000815260200160008152602001600081525090565b611a9c898786600360016002612c36565b60408301528152611ab38887866002600181612c36565b6060830152602082015280518951604080840151908c0151611adb9392919060016002612e4a565b611afb8160200151896000015183606001518b6060015160016002612e4a565b8851611b0690611d58565b611b235760405163188da0b360e21b815260040160405180910390fd5b8751611b2e90611d58565b611b4b5760405163188da0b360e21b815260040160405180910390fd5b87600001516001600160a01b031689600001516001600160a01b03167f22aceb00bb2ded7eb0facc722fa061971f5950b739ecb216300e019b74ccd4a08b604001518c606001518b8b8a8a8a604051602001611bb1929190918252602082015260400190565b60408051601f1981840301815282825280516020918201206001600160a01b03988916845296909716968201969096526001600160401b0393909316838601526001600160701b03918216606084015216608082015260a081019190915290519081900360c00190a35050505050611c296001603955565b50505050565b6000805b600354811015611c8d57836001600160a01b031660038281548110611c5a57611c5a615720565b6000918252602090912001546001600160a01b031603611c7d5760019150611c8d565b611c8681615753565b9050611c33565b5080611cac5760405163a87fa00b60e01b815260040160405180910390fd5b6000611cb786613005565b6040516309f70f0560e31b815290915073dc91af7b038cc8760a0c53814f21be09caace0d790634fb8782890611d029060039060029060019060049088908d908d908d9085016159da565b60006040518083038186803b158015611d1a57600080fd5b505af4158015611d2e573d6000803e3d6000fd5b50505050505050505050565b611d42612296565b611d4e6000348361298e565b6112f76001603955565b6001600160a01b0381166000908152600260205260408120548103611d7f57506001919050565b6000611d8a836121d2565b516004811115611d9c57611d9c6150f7565b1492915050565b61130c33611d3a565b606082516001600160401b03811115611dc757611dc76142fb565b604051908082528060200260200182016040528015611df0578160200160208202803683370190505b50905060005b8351811015611e9d576001600160a01b03831660009081526001602052604081208551909190869084908110611e2e57611e2e615720565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a900460170b828281518110611e7657611e76615720565b602002602001019060170b908160170b815250508080611e9590615753565b915050611df6565b5092915050565b6001600160a01b0380821660009081526001602090815260408083209386168352929052205460170b5b92915050565b611edc612847565b611ee860038787614251565b506005805460ff95861661ffff199091161761010094909516939093029390931762010000600160901b031916620100006001600160401b0392831602600160501b600160901b03191617600160501b93909116929092029190911790555050565b611f52612847565b600680546001600160a01b039586166001600160a01b0319918216179091556007805494861694821694909417909355600880549285169284169290921790915560a28054919093169116179055565b60405163e7e652ed60e01b815260009073e8c2ac36f9e689f0b4b7c52d80b42fdfbd6645679063e7e652ed90611fdc908590600401615a3c565b6040805180830381865af4158015611ff8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e9d9190615a5f565b6008546001600160a01b03163314612047576040516387ece76560e01b815260040160405180910390fd5b61143582826122ef565b612059612847565b6001600160a01b0381166120be5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016113ae565b6112f7816128a6565b6001600160a01b0381166000908152600260209081526040808320805482518185028101850190935280835260609492939192909184015b82821015612168576000848152602090819020604080516060810182526002860290920180546001600160a01b0381168452600160a01b90046001600160401b0316838501526001908101546001600160c01b03169183019190915290835290920191016120ff565b505050509050919050565b6002602052816000526040600020818154811061218f57600080fd5b6000918252602090912060029091020180546001909101546001600160a01b0382169350600160a01b9091046001600160401b031691506001600160c01b031683565b6121fe604080516080810190915280600081526020016000815260200160008152602001600081525090565b600061220983613005565b60405163f127baa160e01b815290915073dc91af7b038cc8760a0c53814f21be09caace0d79063f127baa19061224e9060039060029060019060049088908201615a8b565b608060405180830381865af415801561226b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061228f9190615ab6565b9392505050565b6002603954036122e85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016113ae565b6002603955565b6122f7612296565b6040805163c568f68560e01b81526001600160a01b03808516600483015283518116602483015260208401516001600160401b0390811660448401529284015116606482015260608301519091166084820152608082015162ffffff1660a482015260a082015160c482015260a360e4820152600161010482015260026101248201527359723329a1e906629516b30ca9c30400b11a1ecd9063c568f685906101440160006040518083038186803b1580156123b257600080fd5b505af41580156123c6573d6000803e3d6000fd5b505050506123d382611d58565b6123f05760405163188da0b360e21b815260040160405180910390fd5b7f8156ea5248573c664d8d0995ccb941c2cd87e2e6fa12d8a56594295bae8d14bb816000015182604001518360a0015160405161242f939291906156d2565b60405180910390a16114356001603955565b60008061244d866130a8565b92509050806124845760405162461bcd60e51b815260206004820152600360248201526222992160e91b60448201526064016113ae565b60006305f5e1008760c001516001600160401b0316866124a4919061576c565b6124ae919061578b565b905060008088610140015160ff1660010361252b5787600001516001600160a01b031689606001516001600160a01b0316148015612505575087602001516001600160a01b031689604001516001600160a01b0316145b6125215760405162461bcd60e51b81526004016113ae90615b28565b508190508561258f565b87600001516001600160a01b031689604001516001600160a01b031614801561256d575087602001516001600160a01b031689606001516001600160a01b0316145b6125895760405162461bcd60e51b81526004016113ae90615b28565b50859050815b87606001516001600160a01b031689600001516001600160a01b0316146125ec5760405162461bcd60e51b815260206004820152601160248201527024b731b7b93932b1ba2932b1b2b4bb32b960791b60448201526064016113ae565b8760800151821461260f5760405162461bcd60e51b81526004016113ae90615b46565b8760a001518110156126335760405162461bcd60e51b81526004016113ae90615b46565b8860a001516001600160401b03168711156126785760405162461bcd60e51b81526020600482015260056024820152642299a0b6a160d91b60448201526064016113ae565b856103e88a610120015161268c91906152f0565b6001600160401b031610156126c95760405162461bcd60e51b8152602060048201526003602482015262229a2160e91b60448201526064016113ae565b88608001516001600160a01b031688602001516001600160a01b031603612707578860e001516001600160401b03168860a001511161270757600080fd5b50505050949350505050565b612740816000015182608001518360e001516001600160401b031661273790615b6f565b60016002613272565b5080516001600160a01b039081166000908152600160209081526040808320608086015190941683529290529081205460170b121561279257604051631e9acf1760e31b815260040160405180910390fd5b611435816020015182608001518360e001516001600160401b031660016002613272565b6000838152602081905260409020546001600160c01b03166127e16001600160701b03831682615b8b565b9050826001600160c01b0316816001600160c01b03161115612816576040516341a26a6360e01b815260040160405180910390fd5b60009384526020849052604090932080546001600160c01b0319166001600160c01b03909416939093179092555050565b3361285061159c565b6001600160a01b03161461130c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016113ae565b606d80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b03163b151590565b603a54610100900460ff1661292e5760405162461bcd60e51b81526004016113ae90615bad565b61130c613471565b611c29846323b872dd60e01b858585604051602401612957939291906156d2565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526134a1565b6001600160a01b03808216600090815260016020908152604080832093871683529290529081205460170b8113906129cf856001600160701b038616612b09565b6001600160a01b038085166000908152600160209081526040808320938a16835292905290812080549293506001600160701b03841692909190612a1790849060170b6155d1565b92506101000a8154816001600160c01b03021916908360170b6001600160c01b03160217905550600080516020615f3e8339815191528386836001600160701b0316604051612a689392919061557f565b60405180910390a16001600160701b03841615612ac857846001600160a01b0316836001600160a01b0316336001600160a01b0316600080516020615f5e83398151915260018542604051612abf939291906156f6565b60405180910390a45b811561187d576001600160a01b0380841660009081526001602090815260408083209389168352929052205461187d9084908790600290859060170b613576565b6000806001600160a01b038416612b4157612b3a670de0b6b3a7640000612b34856305f5e100613720565b9061372c565b9050612bca565b6000846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba59190615bf8565b60ff169050612bc6612bb882600a615cf9565b612b34866305f5e100613720565b9150505b60016001606f1b03811061228f5760405162461bcd60e51b815260206004820152600360248201526245335560e81b60448201526064016113ae565b6040516001600160a01b03831660248201526044810182905261158c90849063a9059cbb60e01b90606401612957565b60008060018516156002861615801590612c93578960a001516001600160401b0316896001600160701b03168b60e001516001600160401b0316612c7a919061576c565b612c84919061578b565b6001600160401b031660e08b01525b506001600160701b03881615612e3e5760408051608081018252600080825260208201819052918101829052606081019190915281612cf057612ce16001600160701b038916600019615d05565b896001600160701b0316612d10565b612d056001600160701b038a16600019615d05565b886001600160701b03165b6040830152815281612d2b5789606001518a60400151612d36565b89604001518a606001515b6001600160a01b0390811660608401529081166020830181905260808c01516001921603612d85578a60e001516001600160401b031682600001818151612d7d9190615d8a565b905250612df7565b81606001516001600160a01b03168b608001516001600160a01b031603612dc5578a60e001516001600160401b031682604001818151612d7d9190615d8a565b612df48b600001518c608001518d60e001516001600160401b0316600019612ded9190615d05565b8a8a613272565b90505b8a5160208301518351612e0d9291908a8a613272565b8116945081604001519350612e3a8b602001518c608001518d60e001516001600160401b03168a8a613272565b5050505b50965096945050505050565b6001600160a01b0380861660009081526020848152604080832093871683529290529081205460170b90612e7e8287615dc9565b905060008612158015612e915750818112155b80612ea65750600086128015612ea657508181125b612ec25760405162461bcd60e51b81526004016113ae90615e0a565b600088158015612ed957506001600160a01b038616155b8015612ef55750662386f26fc10000886001600160a01b031631105b15612eff57600198505b886001148015612f0f5750600087135b8015612f1b5750600082135b15612fd7576000612f2c8789613738565b6001600160701b0316905060006001600160a01b03881615612fba576040516370a0823160e01b81526001600160a01b038916906370a0823190612f74903090600401614bc1565b602060405180830381865afa158015612f91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fb59190615682565b612fbc565b475b9050818110612fd457612fd0888b846137e9565b8892505b50505b6000612fe38289615d8a565b90508015611d2e57612ff88988838989613272565b5050505050505050505050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152506040805160e0810182526001600160a01b03928316815260075483166020820152600654909216908201526005546001600160401b03600160501b82048116606084015262010000820416608083015260ff80821660a08401526101009091041660c082015290565b60008060006130b6846138ab565b90506000604051602001613121907f454950373132446f6d61696e28737472696e67206e616d652c737472696e672081527f76657273696f6e2c75696e7432353620636861696e49642c627974657333322060208201526473616c742960d81b604082015260450190565b60408051601f1981840301815282825280516020918201208383018352600e84526d4f72696f6e2045786368616e676560901b93820193909352815180830183526001808252603160f81b918301919091528251808301949094527fabed71ea2445a13c99e4e4afa62b708e9aaf6cd13041ba86482e6cbf83fbc4cf848401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606085015260808401527ff2d857f4a3edcb9b78b4d503bfe733db1e3f6cdc2b7971ee739626c97e86a55760a0808501919091528251808503909101815260c08401835280519082012061190160f01b60e085015260e284015261010280840186905282518085039091018152610122909301909152815191012085516101608701519192506001600160a01b03169061325d908390613a39565b6001600160a01b031614959194509092505050565b6001600160a01b0380861660009081526020848152604080832093881683529290529081205460170b816132a68683615dc9565b9050600086121580156132b95750818112155b806132ce57506000861280156132ce57508181125b6132ea5760405162461bcd60e51b81526004016113ae90615e0a565b6000861380156132fa5750600082125b156133115761330c8888868985613576565b613386565b600082121580156133225750600081125b15613386576001600160a01b038716156133595761334c8761334683600019615d05565b8a613a5d565b6133569082615dc9565b90505b600081121561336e5761330c88888387613b87565b6000821361337d576001613380565b60005b60ff1692505b808214613466576001600160bf1b031981128015906133ac57506001600160bf1b038113155b6133c85760405162461bcd60e51b81526004016113ae90615e0a565b6001600160a01b03808916600090815260208781526040808320938b168352929052908120546133fb9060170b8361550c565b6001600160a01b038a8116600090815260208981526040808320938d16835292905281902080546001600160c01b0319166001600160c01b03861617905551909150600080516020615f3e8339815191529061345c908b908b90859061557f565b60405180910390a1505b505095945050505050565b603a54610100900460ff166134985760405162461bcd60e51b81526004016113ae90615bad565b61130c336128a6565b60006134f6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613c639092919063ffffffff16565b90508051600014806135175750808060200190518101906135179190615e27565b61158c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016113ae565b60008160170b126135915761358c858585613c7a565b61187d565b6001600160a01b0385166000908152602084905260408120545b6135b66001826154a8565b82101561361d576001600160a01b038781166000908152602087905260409020805491881691849081106135ec576135ec615720565b60009182526020909120600290910201546001600160a01b03161461361d578161361581615753565b9250506135ab565b6001600160a01b038716600090815260208690526040812080548490811061364757613647615720565b6000918252602090912060029091020160018101549091506001600160c01b03166001600160701b038616106136c9576136808461555c565b6001820180546001600160c01b0319166001600160c01b03929092169190911790558054600160a01b600160e01b031916600160a01b426001600160401b031602178155610bd4565b6001810180546001600160701b03871691906000906136f29084906001600160c01b0316615e42565b92506101000a8154816001600160c01b0302191690836001600160c01b031602179055505050505050505050565b600061228f828461576c565b600061228f828461578b565b6000806001600160a01b03841661376357612b3a6305f5e100612b3485670de0b6b3a7640000613720565b6000846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137c79190615bf8565b60ff169050612bc66305f5e100612b346137e284600a615cf9565b8790613720565b801561158c576137f883613e5d565b15613897578047101561381e57604051631e9acf1760e31b815260040160405180910390fd5b6000826001600160a01b03168261138890604051600060405180830381858888f193505050503d8060008114613870576040519150601f19603f3d011682016040523d82523d6000602084013e613875565b606091505b5050905080611c295760405163b12d13eb60e01b815260040160405180910390fd5b61158c6001600160a01b0384168383612c06565b60006040516020016139b1907f4f7264657228616464726573732073656e646572416464726573732c6164647281527f657373206d617463686572416464726573732c6164647265737320626173654160208201527f737365742c616464726573732071756f746541737365742c616464726573732060408201527f6d61746368657246656541737365742c75696e74363420616d6f756e742c756960608201527f6e7436342070726963652c75696e743634206d6174636865724665652c75696e60808201527f743634206e6f6e63652c75696e7436342065787069726174696f6e2c75696e7460a0820152693820627579536964652960b01b60c082015260ca0190565b60405160208183030381529060405280519060200120826000015183602001518460400151856060015186608001518760a001518860c001518960e001518a61010001518b61012001518c6101400151604051602001613a1c9c9b9a99989796959493929190615e6a565b604051602081830303815290604052805190602001209050919050565b6000806000613a488585613e96565b91509150613a5581613edb565b509392505050565b600080613a6a8585613738565b6001600160701b0316905080856001600160a01b03166370a08231856040518263ffffffff1660e01b8152600401613aa29190614bc1565b602060405180830381865afa158015613abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ae39190615682565b10158015613b625750604051636eb1769f60e11b81526001600160a01b03848116600483015230602483015282919087169063dd62ed3e90604401602060405180830381865afa158015613b3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b5f9190615682565b10155b15613b7c57613b7385843084612936565b8391505061228f565b506000949350505050565b806000856001600160a01b03166001600160a01b031681526020019081526020016000206040518060600160405280856001600160a01b03168152602001426001600160401b0316815260200184613bde9061555c565b6001600160c01b03908116909152825460018181018555600094855260209485902084516002909302018054958501516001600160401b0316600160a01b026001600160e01b03199096166001600160a01b0390931692909217949094178155604090920151919092018054919092166001600160c01b031990911617905550505050565b6060613c728484600085614020565b949350505050565b6001600160a01b038316600090815260208290526040812054905b8181101561187d576001600160a01b03858116600090815260208590526040902080549186169183908110613ccc57613ccc615720565b60009182526020909120600290910201546001600160a01b031603613e4b576001821115613de9576001600160a01b0385166000908152602084905260409020613d176001846154a8565b81548110613d2757613d27615720565b9060005260206000209060020201836000876001600160a01b03166001600160a01b031681526020019081526020016000208281548110613d6a57613d6a615720565b60009182526020909120825460029092020180546001600160a01b031981166001600160a01b03909316928317825583546001600160401b03600160a01b9182900416026001600160e01b0319909116909217919091178155600191820154910180546001600160c01b0319166001600160c01b039092169190911790555b6001600160a01b0385166000908152602084905260409020805480613e1057613e10615ef8565b60008281526020902060026000199092019182020180546001600160e01b031916815560010180546001600160c01b0319169055905561187d565b80613e5581615753565b915050613c95565b60006001600160a01b0382161580611ece57506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1492915050565b6000808251604103613ecc5760208301516040840151606085015160001a613ec0878285856140fb565b94509450505050613ed4565b506000905060025b9250929050565b6000816004811115613eef57613eef6150f7565b03613ef75750565b6001816004811115613f0b57613f0b6150f7565b03613f535760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b60448201526064016113ae565b6002816004811115613f6757613f676150f7565b03613fb45760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016113ae565b6003816004811115613fc857613fc86150f7565b036112f75760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016113ae565b6060824710156140815760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016113ae565b600080866001600160a01b0316858760405161409d9190615f0e565b60006040518083038185875af1925050503d80600081146140da576040519150601f19603f3d011682016040523d82523d6000602084013e6140df565b606091505b50915091506140f0878383876141b5565b979650505050505050565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b0383111561412857506000905060036141ac565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561417c573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166141a5576000600192509250506141ac565b9150600090505b94509492505050565b6060831561422257825160000361421b576141cf856128f8565b61421b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016113ae565b5081613c72565b613c7283838151156142375781518083602001fd5b8060405162461bcd60e51b81526004016113ae9190615f2a565b8280548282559060005260206000209081019282156142a4579160200282015b828111156142a45781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614271565b506142b09291506142b4565b5090565b5b808211156142b057600081556001016142b5565b6001600160401b03169052565b6001600160a01b03811681146112f757600080fd5b80356142f6816142d6565b919050565b634e487b7160e01b600052604160045260246000fd5b60405161018081016001600160401b0381118282101715614334576143346142fb565b60405290565b604051606081016001600160401b0381118282101715614334576143346142fb565b604051601f8201601f191681016001600160401b0381118282101715614384576143846142fb565b604052919050565b600060e0828403121561439e57600080fd5b60405160e081016001600160401b03811182821017156143c0576143c06142fb565b60405290508082356143d1816142d6565b815260208301356143e1816142d6565b602082015260408301356143f4816142d6565b60408201526060830135614407816142d6565b806060830152506080830135608082015260a083013560a082015260c083013560c08201525092915050565b60008083601f84011261444557600080fd5b5081356001600160401b0381111561445c57600080fd5b602083019150836020828501011115613ed457600080fd5b600080600080600080610140878903121561448e57600080fd5b8635614499816142d6565b95506144a8886020890161438c565b94506101008701356001600160401b03808211156144c557600080fd5b6144d18a838b01614433565b90965094506101208901359150808211156144eb57600080fd5b506144f889828a01614433565b979a9699509497509295939492505050565b80356001600160401b03811681146142f657600080fd5b60ff811681146112f757600080fd5b80356142f681614521565b600082601f83011261454c57600080fd5b81356001600160401b03811115614565576145656142fb565b614578601f8201601f191660200161435c565b81815284602083860101111561458d57600080fd5b816020850160208301376000918101602001919091529392505050565b600061018082840312156145bd57600080fd5b6145c5614311565b90506145d0826142eb565b81526145de602083016142eb565b60208201526145ef604083016142eb565b6040820152614600606083016142eb565b6060820152614611608083016142eb565b608082015261462260a0830161450a565b60a082015261463360c0830161450a565b60c082015261464460e0830161450a565b60e082015261010061465781840161450a565b9082015261012061466983820161450a565b9082015261014061467b838201614530565b90820152610160828101356001600160401b0381111561469a57600080fd5b6146a68582860161453b565b82840152505092915050565b803562ffffff811681146142f657600080fd5b600080600080600060a086880312156146dd57600080fd5b85356001600160401b03808211156146f457600080fd5b908701906060828a03121561470857600080fd5b61471061433a565b82358281111561471f57600080fd5b61472b8b8286016145aa565b82525061473a602084016146b2565b60208201526040830135604082015280975050602088013591508082111561476157600080fd5b5061476e888289016145aa565b94505061477d6040870161450a565b925061478b6060870161450a565b91506147996080870161450a565b90509295509295909350565b600061010082840312156147b857600080fd5b50919050565b6000806000604084860312156147d357600080fd5b83356001600160401b03808211156147ea57600080fd5b6147f6878388016147a5565b9450602086013591508082111561480c57600080fd5b5061481986828701614433565b9497909650939450505050565b80356001600160701b03811681146142f657600080fd5b600080600080600080600080610180808a8c03121561485b57600080fd5b6148648a614826565b985060208a01356001600160401b038082111561488057600080fd5b818c01915082828e03121561489457600080fd5b81995060408c013592506148a7836142d6565b8298506148b78d60608e0161438c565b97506101408c01359250808311156148ce57600080fd5b6148da8d848e01614433565b90975095506101608c01359250869150808311156148f757600080fd5b50506149058b828c01614433565b999c989b5096995094979396929594505050565b6000806000806000806080878903121561493257600080fd5b86356001600160401b038082111561494957600080fd5b6149558a838b016147a5565b9750602089013591508082111561496b57600080fd5b6149778a838b01614433565b9097509550604089013591508082111561499057600080fd5b61499c8a838b016147a5565b945060608901359150808211156144eb57600080fd5b6000602082840312156149c457600080fd5b5035919050565b6000602082840312156149dd57600080fd5b61228f8261450a565b6000602082840312156149f857600080fd5b813561228f816142d6565b600080600080600060608688031215614a1b57600080fd5b8535614a26816142d6565b945060208601356001600160401b0380821115614a4257600080fd5b614a4e89838a01614433565b90965094506040880135915080821115614a6757600080fd5b50614a7488828901614433565b969995985093965092949392505050565b600060c08284031215614a9757600080fd5b60405160c081016001600160401b0381118282101715614ab957614ab96142fb565b6040529050808235614aca816142d6565b8152614ad86020840161450a565b60208201526040830135614aeb816142d6565b6040820152614afc6060840161450a565b6060820152614b0d608084016146b2565b608082015260a083013560a08201525092915050565b600060c08284031215614b3557600080fd5b61228f8383614a85565b60008060408385031215614b5257600080fd5b8235614b5d816142d6565b915061192e60208401614826565b600080600060608486031215614b8057600080fd5b8335614b8b816142d6565b9250614b9960208501614826565b91506040840135614ba9816142d6565b809150509250925092565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b60008083601f840112614be757600080fd5b5081356001600160401b03811115614bfe57600080fd5b6020830191508360208260051b8501011115613ed457600080fd5b60008060008060408587031215614c2f57600080fd5b84356001600160401b0380821115614c4657600080fd5b614c5288838901614bd5565b90965094506020870135915080821115614c6b57600080fd5b50614c7887828801614bd5565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015614cc55783516001600160a01b031683529284019291840191600101614ca0565b50909695505050505050565b60008060408385031215614ce457600080fd5b8235915060208301356001600160401b03811115614d0157600080fd5b614d0d858286016145aa565b9150509250929050565b60008060008060808587031215614d2d57600080fd5b84356001600160401b0380821115614d4457600080fd5b614d50888389016145aa565b95506020870135915080821115614d6657600080fd5b50614d73878288016145aa565b935050614d826040860161450a565b9150614d9060608601614826565b905092959194509250565b60008060008060808587031215614db157600080fd5b8435614dbc816142d6565b93506020850135614dcc816142d6565b92506040850135614d82816142d6565b60008060408385031215614def57600080fd5b82356001600160401b0380821115614e0657600080fd5b818501915085601f830112614e1a57600080fd5b8135602082821115614e2e57614e2e6142fb565b8160051b9250614e3f81840161435c565b8281529284018101928181019089851115614e5957600080fd5b948201945b84861015614e835785359350614e73846142d6565b8382529482019490820190614e5e565b9650614e9290508782016142eb565b9450505050509250929050565b6020808252825182820181905260009190848201906040850190845b81811015614cc557835160170b83529284019291840191600101614ebb565b60008060408385031215614eed57600080fd5b8235614ef8816142d6565b91506020830135614f08816142d6565b809150509250929050565b60008060008060008060a08789031215614f2c57600080fd5b86356001600160401b03811115614f4257600080fd5b614f4e89828a01614bd5565b9097509550506020870135614f6281614521565b93506040870135614f7281614521565b9250614f806060880161450a565b9150614f8e6080880161450a565b90509295509295509295565b60008060008060808587031215614fb057600080fd5b8435614fbb816142d6565b93506020850135614fcb816142d6565b92506040850135614fdb816142d6565b91506060850135614feb816142d6565b939692955090935050565b60006020828403121561500857600080fd5b81356001600160401b0381111561501e57600080fd5b613c72848285016145aa565b60008060e0838503121561503d57600080fd5b8235615048816142d6565b915061192e8460208501614a85565b602080825282518282018190526000919060409081850190868401855b828110156150be57815180516001600160a01b03168552868101516001600160401b0316878601528501516001600160c01b03168585015260609093019290850190600101615074565b5091979650505050505050565b600080604083850312156150de57600080fd5b82356150e9816142d6565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b815160808201906005811061513257634e487b7160e01b600052602160045260246000fd5b8083525060208301516020830152604083015160408301526060830151606083015292915050565b60018060a01b03808251168352806020830151166020840152806040830151166040840152806060830151166060840152506080810151608083015260a081015160a083015260c081015160c08301525050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b038716815260006101606151f5602084018961515a565b8061010084015261520981840187896151ae565b61012084019590955250506101400152949350505050565b6001600160a01b038681168252851660208201526000610140615247604084018761515a565b8061012084015261525b81840185876151ae565b98975050505050505050565b60008060006060848603121561527c57600080fd5b8351925060208401519150604084015190509250925092565b634e487b7160e01b600052601160045260246000fd5b60006001600160401b03828116848216811515828404821116156152d1576152d1615295565b02949350505050565b634e487b7160e01b600052601260045260246000fd5b60006001600160401b038381168061530a5761530a6152da565b92169190910492915050565b6000808335601e1984360301811261532d57600080fd5b83016020810192503590506001600160401b0381111561534c57600080fd5b803603821315613ed457600080fd5b60a081526000873561536c816142d6565b6001600160a01b031660a0830152615386602089016142eb565b61539360c0840182614bb4565b506153a0604089016142eb565b6153ad60e0840182614bb4565b506153ba606089016142eb565b6101006153c981850183614bb4565b6153d560808b0161450a565b91506153e56101208501836142c9565b6153f160a08b0161450a565b91506154016101408501836142c9565b60c08a013561016085015261541960e08b018b615316565b9250816101808601526154316101a0860184836151ae565b92505050828103602084015261544881888a6151ae565b6040840196909652505060608101929092526080909101529392505050565b6001600160a01b0386811682528581166020830152841660408201526080606082018190526000906140f090830184866151ae565b6000611ece36836145aa565b6000828210156154ba576154ba615295565b500390565b8681526001600160a01b0386811660208301528516604082015260006101606154eb606084018761515a565b806101408401526154ff81840185876151ae565b9998505050505050505050565b6000601782810b9084900b82811280156001600160bf1b031983018412161561553757615537615295565b6001600160bf1b038201831381161561555257615552615295565b5090039392505050565b6000601782900b600160bf1b810161557657615576615295565b60000392915050565b6001600160a01b03938416815291909216602082015260179190910b604082015260600190565b60006001600160401b038281168482168083038211156155c8576155c8615295565b01949350505050565b6000601782810b9084900b82821280156001600160bf1b03849003831316156155fc576155fc615295565b6001600160bf1b0319839003821281161561561957615619615295565b50019392505050565b600060018060a01b03808c16835260e0602084015261564560e084018b8d6151ae565b8381036040850152615658818a8c6151ae565b9790911660608401525050608081019390935260a083019190915260c09091015295945050505050565b60006020828403121561569457600080fd5b5051919050565b6001600160a01b038581168252841660208201526060604082018190526000906156c890830184866151ae565b9695505050505050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b92151583526001600160701b039190911660208301526001600160401b0316604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561574857600080fd5b813561228f81614521565b60006001820161576557615765615295565b5060010190565b600081600019048311821515161561578657615786615295565b500290565b60008261579a5761579a6152da565b500490565b60005b838110156157ba5781810151838201526020016157a2565b83811115611c295750506000910152565b600081518084526157e381602086016020860161579f565b601f01601f19169290920160200192915050565b6000610180615807848451614bb4565b60208301516158196020860182614bb4565b50604083015161582c6040860182614bb4565b50606083015161583f6060860182614bb4565b5060808301516158526080860182614bb4565b5060a083015161586560a08601826142c9565b5060c083015161587860c08601826142c9565b5060e083015161588b60e08601826142c9565b506101008084015161589f828701826142c9565b5050610120808401516158b4828701826142c9565b50506101408381015160ff1690850152610160808401518186018390526156c8838701826157cb565b60e0815260006158f060e083018a6157f7565b8281036020840152615902818a6157f7565b6001600160a01b0398891660408501526001600160701b0397909716606084015250506001600160401b0393909316608084015260a083019190915290921660c09092019190915292915050565b6000806040838503121561596357600080fd5b505080516020909101519092909150565b60018060a01b0380825116835280602083015116602084015280604083015116604084015250606081015160018060401b038082166060850152806080840151166080850152505060ff60a08201511660a083015260ff60c08201511660c08301525050565b60006101c082019050898252886020830152876040830152866060830152615a056080830187615974565b6001600160a01b03948516610160830152929093166101808401526001600160701b03166101a09092019190915295945050505050565b60208152600061228f60208301846157f7565b805180151581146142f657600080fd5b60008060408385031215615a7257600080fd5b615a7b83615a4f565b9150602083015190509250929050565b6000610160820190508682528560208301528460408301528360608301526156c86080830184615974565b600060808284031215615ac857600080fd5b604051608081016001600160401b0381118282101715615aea57615aea6142fb565b604052825160058110615afc57600080fd5b808252506020830151602082015260408301516040820152606083015160608201528091505092915050565b6020808252600490820152634533417360e01b604082015260600190565b6020808252600f908201526e125b98dbdc9c9958dd105b5bdd5b9d608a1b604082015260600190565b6000600160ff1b8201615b8457615b84615295565b5060000390565b60006001600160c01b038281168482168083038211156155c8576155c8615295565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b600060208284031215615c0a57600080fd5b815161228f81614521565b600181815b80851115615c50578160001904821115615c3657615c36615295565b80851615615c4357918102915b93841c9390800290615c1a565b509250929050565b600082615c6757506001611ece565b81615c7457506000611ece565b8160018114615c8a5760028114615c9457615cb0565b6001915050611ece565b60ff841115615ca557615ca5615295565b50506001821b611ece565b5060208310610133831016604e8410600b8410161715615cd3575081810a611ece565b615cdd8383615c15565b8060001904821115615cf157615cf1615295565b029392505050565b600061228f8383615c58565b60006001600160ff1b0381841382841380821686840486111615615d2b57615d2b615295565b600160ff1b6000871282811687830589121615615d4a57615d4a615295565b60008712925087820587128484161615615d6657615d66615295565b87850587128184161615615d7c57615d7c615295565b505050929093029392505050565b60008083128015600160ff1b850184121615615da857615da8615295565b6001600160ff1b0384018313811615615dc357615dc3615295565b50500390565b600080821280156001600160ff1b0384900385131615615deb57615deb615295565b600160ff1b8390038412811615615e0457615e04615295565b50500190565b60208082526003908201526245313160e81b604082015260600190565b600060208284031215615e3957600080fd5b61228f82615a4f565b60006001600160c01b0383811690831681811015615e6257615e62615295565b039392505050565b8c81526001600160a01b038c811660208301528b811660408301528a811660608301528981166080830152881660a08201526001600160401b0387811660c083015286811660e083015285166101008201526101808101615ecf6101208301866142c9565b615edd6101408301856142c9565b60ff83166101608301529d9c50505050505050505050505050565b634e487b7160e01b600052603160045260246000fd5b60008251615f2081846020870161579f565b9190910192915050565b60208152600061228f60208301846157cb56fe2210a3e9136294756a7a989c32de6a75280c32dafeccc1280adcc9bb469f44a3565d6850cdd88f91fbcedecc61c18940ca739db2f1a27fc66ac9d4b236db81b8a26469706673582212202a3e234e5fdafae8c139081886d76210ecc5dd17e3d18f6f58b16b1039c5524764736f6c634300080f0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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.