Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
Value | ||||
---|---|---|---|---|---|---|---|---|---|
0x60806040 | 13113367 | 1044 days ago | IN | Create: ExchangeV3 | 0 ETH | 0.25299454 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
ExchangeV3
Compiler Version
v0.7.6+commit.7338295f
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2021-08-28 */ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; // File: contracts/lib/AddressUtil.sol // Copyright 2017 Loopring Technology Limited. /// @title Utility Functions for addresses /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library AddressUtil { using AddressUtil for *; function isContract( address addr ) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(addr) } return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); } function toPayable( address addr ) internal pure returns (address payable) { return payable(addr); } // Works like address.send but with a customizable gas limit // Make sure your code is safe for reentrancy when using this function! function sendETH( address to, uint amount, uint gasLimit ) internal returns (bool success) { if (amount == 0) { return true; } address payable recipient = to.toPayable(); /* solium-disable-next-line */ (success, ) = recipient.call{value: amount, gas: gasLimit}(""); } // Works like address.transfer but with a customizable gas limit // Make sure your code is safe for reentrancy when using this function! function sendETHAndVerify( address to, uint amount, uint gasLimit ) internal returns (bool success) { success = to.sendETH(amount, gasLimit); require(success, "TRANSFER_FAILURE"); } // Works like call but is slightly more efficient when data // needs to be copied from memory to do the call. function fastCall( address to, uint gasLimit, uint value, bytes memory data ) internal returns (bool success, bytes memory returnData) { if (to != address(0)) { assembly { // Do the call success := call(gasLimit, to, value, add(data, 32), mload(data), 0, 0) // Copy the return data let size := returndatasize() returnData := mload(0x40) mstore(returnData, size) returndatacopy(add(returnData, 32), 0, size) // Update free memory pointer mstore(0x40, add(returnData, add(32, size))) } } } // Like fastCall, but throws when the call is unsuccessful. function fastCallAndVerify( address to, uint gasLimit, uint value, bytes memory data ) internal returns (bytes memory returnData) { bool success; (success, returnData) = fastCall(to, gasLimit, value, data); if (!success) { assembly { revert(add(returnData, 32), mload(returnData)) } } } } // File: contracts/lib/EIP712.sol // Copyright 2017 Loopring Technology Limited. library EIP712 { struct Domain { string name; string version; address verifyingContract; } bytes32 constant internal EIP712_DOMAIN_TYPEHASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); string constant internal EIP191_HEADER = "\x19\x01"; function hash(Domain memory domain) internal pure returns (bytes32) { uint _chainid; assembly { _chainid := chainid() } return keccak256( abi.encode( EIP712_DOMAIN_TYPEHASH, keccak256(bytes(domain.name)), keccak256(bytes(domain.version)), _chainid, domain.verifyingContract ) ); } function hashPacked( bytes32 domainSeparator, bytes32 dataHash ) internal pure returns (bytes32) { return keccak256( abi.encodePacked( EIP191_HEADER, domainSeparator, dataHash ) ); } } // File: contracts/lib/ERC20SafeTransfer.sol // Copyright 2017 Loopring Technology Limited. /// @title ERC20 safe transfer /// @dev see https://github.com/sec-bit/badERC20Fix /// @author Brecht Devos - <[email protected]> library ERC20SafeTransfer { function safeTransferAndVerify( address token, address to, uint value ) internal { safeTransferWithGasLimitAndVerify( token, to, value, gasleft() ); } function safeTransfer( address token, address to, uint value ) internal returns (bool) { return safeTransferWithGasLimit( token, to, value, gasleft() ); } function safeTransferWithGasLimitAndVerify( address token, address to, uint value, uint gasLimit ) internal { require( safeTransferWithGasLimit(token, to, value, gasLimit), "TRANSFER_FAILURE" ); } function safeTransferWithGasLimit( address token, address to, uint value, uint gasLimit ) internal returns (bool) { // A transfer is successful when 'call' is successful and depending on the token: // - No value is returned: we assume a revert when the transfer failed (i.e. 'call' returns false) // - A single boolean is returned: this boolean needs to be true (non-zero) // bytes4(keccak256("transfer(address,uint256)")) = 0xa9059cbb bytes memory callData = abi.encodeWithSelector( bytes4(0xa9059cbb), to, value ); (bool success, ) = token.call{gas: gasLimit}(callData); return checkReturnValue(success); } function safeTransferFromAndVerify( address token, address from, address to, uint value ) internal { safeTransferFromWithGasLimitAndVerify( token, from, to, value, gasleft() ); } function safeTransferFrom( address token, address from, address to, uint value ) internal returns (bool) { return safeTransferFromWithGasLimit( token, from, to, value, gasleft() ); } function safeTransferFromWithGasLimitAndVerify( address token, address from, address to, uint value, uint gasLimit ) internal { bool result = safeTransferFromWithGasLimit( token, from, to, value, gasLimit ); require(result, "TRANSFER_FAILURE"); } function safeTransferFromWithGasLimit( address token, address from, address to, uint value, uint gasLimit ) internal returns (bool) { // A transferFrom is successful when 'call' is successful and depending on the token: // - No value is returned: we assume a revert when the transfer failed (i.e. 'call' returns false) // - A single boolean is returned: this boolean needs to be true (non-zero) // bytes4(keccak256("transferFrom(address,address,uint256)")) = 0x23b872dd bytes memory callData = abi.encodeWithSelector( bytes4(0x23b872dd), from, to, value ); (bool success, ) = token.call{gas: gasLimit}(callData); return checkReturnValue(success); } function checkReturnValue( bool success ) internal pure returns (bool) { // A transfer/transferFrom is successful when 'call' is successful and depending on the token: // - No value is returned: we assume a revert when the transfer failed (i.e. 'call' returns false) // - A single boolean is returned: this boolean needs to be true (non-zero) if (success) { assembly { switch returndatasize() // Non-standard ERC20: nothing is returned so if 'call' was successful we assume the transfer succeeded case 0 { success := 1 } // Standard ERC20: a single boolean value is returned which needs to be true case 32 { returndatacopy(0, 0, 32) success := mload(0) } // None of the above: not successful default { success := 0 } } } return success; } } // File: contracts/lib/MathUint.sol // Copyright 2017 Loopring Technology Limited. /// @title Utility Functions for uint /// @author Daniel Wang - <[email protected]> library MathUint { using MathUint for uint; function mul( uint a, uint b ) internal pure returns (uint c) { c = a * b; require(a == 0 || c / a == b, "MUL_OVERFLOW"); } function sub( uint a, uint b ) internal pure returns (uint) { require(b <= a, "SUB_UNDERFLOW"); return a - b; } function add( uint a, uint b ) internal pure returns (uint c) { c = a + b; require(c >= a, "ADD_OVERFLOW"); } function add64( uint64 a, uint64 b ) internal pure returns (uint64 c) { c = a + b; require(c >= a, "ADD_OVERFLOW"); } } // File: contracts/lib/ReentrancyGuard.sol // Copyright 2017 Loopring Technology Limited. /// @title ReentrancyGuard /// @author Brecht Devos - <[email protected]> /// @dev Exposes a modifier that guards a function against reentrancy /// Changing the value of the same storage value multiple times in a transaction /// is cheap (starting from Istanbul) so there is no need to minimize /// the number of times the value is changed contract ReentrancyGuard { //The default value must be 0 in order to work behind a proxy. uint private _guardValue; // Use this modifier on a function to prevent reentrancy modifier nonReentrant() { // Check if the guard value has its original value require(_guardValue == 0, "REENTRANCY"); // Set the value to something else _guardValue = 1; // Function body _; // Set the value back _guardValue = 0; } } // File: contracts/thirdparty/erc1155/IERC1155Receiver.sol //import "../erc165/ERC165.sol"; /** * _Available since v3.1._ */ interface IERC1155Receiver/* is IERC165*/ { /** @dev Handles the receipt of a single ERC1155 token type. This function is called at the end of a `safeTransferFrom` after the balance has been updated. To accept the transfer, this must return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61, or its own function selector). @param operator The address which initiated the transfer (i.e. msg.sender) @param from The address which previously owned the token @param id The ID of the token being transferred @param value The amount of tokens being transferred @param data Additional data with no specified format @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns(bytes4); /** @dev Handles the receipt of a multiple ERC1155 token types. This function is called at the end of a `safeBatchTransferFrom` after the balances have been updated. To accept the transfer(s), this must return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81, or its own function selector). @param operator The address which initiated the batch transfer (i.e. msg.sender) @param from The address which previously owned the token @param ids An array containing ids of each token being transferred (order and length must match values array) @param values An array containing amounts of each token being transferred (order and length must match ids array) @param data Additional data with no specified format @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns(bytes4); } // File: contracts/thirdparty/erc1155/ERC1155Receiver.sol //import "../erc165/ERC165.sol"; /** * @dev _Available since v3.1._ */ abstract contract ERC1155Receiver is /*ERC165, */IERC1155Receiver { /*constructor() { _registerInterface( ERC1155Receiver(address(0)).onERC1155Received.selector ^ ERC1155Receiver(address(0)).onERC1155BatchReceived.selector ); }*/ } // File: contracts/thirdparty/erc1155/ERC1155Holder.sol /** * @dev _Available since v3.1._ */ contract ERC1155Holder is ERC1155Receiver { function onERC1155Received(address, address, uint256, uint256, bytes memory) public virtual override returns (bytes4) { return this.onERC1155Received.selector; } function onERC1155BatchReceived(address, address, uint256[] memory, uint256[] memory, bytes memory) public virtual override returns (bytes4) { return this.onERC1155BatchReceived.selector; } } // File: contracts/thirdparty/erc721/IERC721Receiver.sol /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. */ function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4); } // File: contracts/thirdparty/erc721/ERC721Holder.sol /** * @dev Implementation of the {IERC721Receiver} interface. * * Accepts all token transfers. * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. */ contract ERC721Holder is IERC721Receiver { /** * @dev See {IERC721Receiver-onERC721Received}. * * Always returns `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { return this.onERC721Received.selector; } } // File: contracts/thirdparty/proxies/Proxy.sol // This code is taken from https://github.com/OpenZeppelin/openzeppelin-labs // with minor modifications. /** * @title Proxy * @dev Gives the possibility to delegate any call to a foreign implementation. */ abstract contract Proxy { /** * @dev Tells the address of the implementation where every call will be delegated. * @return impl address of the implementation to which it will be delegated */ function implementation() public view virtual returns (address impl); receive() payable external { _fallback(); } fallback() payable external { _fallback(); } function _fallback() private { address _impl = implementation(); require(_impl != address(0)); assembly { let ptr := mload(0x40) calldatacopy(ptr, 0, calldatasize()) let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0) let size := returndatasize() returndatacopy(ptr, 0, size) switch result case 0 { revert(ptr, size) } default { return(ptr, size) } } } } // File: contracts/thirdparty/proxies/UpgradabilityProxy.sol // This code is taken from https://github.com/OpenZeppelin/openzeppelin-labs // with minor modifications. /** * @title UpgradeabilityProxy * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded */ contract UpgradeabilityProxy is Proxy { /** * @dev This event will be emitted every time the implementation gets upgraded * @param implementation representing the address of the upgraded implementation */ event Upgraded(address indexed implementation); // Storage position of the address of the current implementation bytes32 private constant implementationPosition = keccak256("org.zeppelinos.proxy.implementation"); /** * @dev Constructor function */ constructor() {} /** * @dev Tells the address of the current implementation * @return impl address of the current implementation */ function implementation() public view override returns (address impl) { bytes32 position = implementationPosition; assembly { impl := sload(position) } } /** * @dev Sets the address of the current implementation * @param newImplementation address representing the new implementation to be set */ function setImplementation(address newImplementation) internal { bytes32 position = implementationPosition; assembly { sstore(position, newImplementation) } } /** * @dev Upgrades the implementation address * @param newImplementation representing the address of the new implementation to be set */ function _upgradeTo(address newImplementation) internal { address currentImplementation = implementation(); require(currentImplementation != newImplementation); setImplementation(newImplementation); emit Upgraded(newImplementation); } } // File: contracts/thirdparty/proxies/OwnedUpgradabilityProxy.sol // This code is taken from https://github.com/OpenZeppelin/openzeppelin-labs // with minor modifications. /** * @title OwnedUpgradabilityProxy * @dev This contract combines an upgradeability proxy with basic authorization control functionalities */ contract OwnedUpgradabilityProxy is UpgradeabilityProxy { /** * @dev Event to show ownership has been transferred * @param previousOwner representing the address of the previous owner * @param newOwner representing the address of the new owner */ event ProxyOwnershipTransferred(address previousOwner, address newOwner); // Storage position of the owner of the contract bytes32 private constant proxyOwnerPosition = keccak256("org.zeppelinos.proxy.owner"); /** * @dev the constructor sets the original owner of the contract to the sender account. */ constructor() { setUpgradabilityOwner(msg.sender); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyProxyOwner() { require(msg.sender == proxyOwner()); _; } /** * @dev Tells the address of the owner * @return owner the address of the owner */ function proxyOwner() public view returns (address owner) { bytes32 position = proxyOwnerPosition; assembly { owner := sload(position) } } /** * @dev Sets the address of the owner */ function setUpgradabilityOwner(address newProxyOwner) internal { bytes32 position = proxyOwnerPosition; assembly { sstore(position, newProxyOwner) } } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferProxyOwnership(address newOwner) public onlyProxyOwner { require(newOwner != address(0)); emit ProxyOwnershipTransferred(proxyOwner(), newOwner); setUpgradabilityOwner(newOwner); } /** * @dev Allows the proxy owner to upgrade the current version of the proxy. * @param implementation representing the address of the new implementation to be set. */ function upgradeTo(address implementation) public onlyProxyOwner { _upgradeTo(implementation); } /** * @dev Allows the proxy owner to upgrade the current version of the proxy and call the new implementation * to initialize whatever is needed through a low level call. * @param implementation representing the address of the new implementation to be set. * @param data represents the msg.data to bet sent in the low level call. This parameter may include the function * signature of the implementation to be called with the needed payload */ function upgradeToAndCall(address implementation, bytes memory data) payable public onlyProxyOwner { upgradeTo(implementation); (bool success, ) = address(this).call{value: msg.value}(data); require(success); } } // File: contracts/core/iface/IAgentRegistry.sol // Copyright 2017 Loopring Technology Limited. interface IAgent{} abstract contract IAgentRegistry { /// @dev Returns whether an agent address is an agent of an account owner /// @param owner The account owner. /// @param agent The agent address /// @return True if the agent address is an agent for the account owner, else false function isAgent( address owner, address agent ) external virtual view returns (bool); /// @dev Returns whether an agent address is an agent of all account owners /// @param owners The account owners. /// @param agent The agent address /// @return True if the agent address is an agent for the account owner, else false function isAgent( address[] calldata owners, address agent ) external virtual view returns (bool); /// @dev Returns whether an agent address is a universal agent. /// @param agent The agent address /// @return True if the agent address is a universal agent, else false function isUniversalAgent(address agent) public virtual view returns (bool); } // File: contracts/lib/Ownable.sol // Copyright 2017 Loopring Technology Limited. /// @title Ownable /// @author Brecht Devos - <[email protected]> /// @dev The Ownable contract has an owner address, and provides basic /// authorization control functions, this simplifies the implementation of /// "user permissions". contract Ownable { address public owner; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /// @dev The Ownable constructor sets the original `owner` of the contract /// to the sender. constructor() { owner = msg.sender; } /// @dev Throws if called by any account other than the owner. modifier onlyOwner() { require(msg.sender == owner, "UNAUTHORIZED"); _; } /// @dev Allows the current owner to transfer control of the contract to a /// new owner. /// @param newOwner The address to transfer ownership to. function transferOwnership( address newOwner ) public virtual onlyOwner { require(newOwner != address(0), "ZERO_ADDRESS"); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } function renounceOwnership() public onlyOwner { emit OwnershipTransferred(owner, address(0)); owner = address(0); } } // File: contracts/lib/Claimable.sol // Copyright 2017 Loopring Technology Limited. /// @title Claimable /// @author Brecht Devos - <[email protected]> /// @dev Extension for the Ownable contract, where the ownership needs /// to be claimed. This allows the new owner to accept the transfer. contract Claimable is Ownable { address public pendingOwner; /// @dev Modifier throws if called by any account other than the pendingOwner. modifier onlyPendingOwner() { require(msg.sender == pendingOwner, "UNAUTHORIZED"); _; } /// @dev Allows the current owner to set the pendingOwner address. /// @param newOwner The address to transfer ownership to. function transferOwnership( address newOwner ) public override onlyOwner { require(newOwner != address(0) && newOwner != owner, "INVALID_ADDRESS"); pendingOwner = newOwner; } /// @dev Allows the pendingOwner address to finalize the transfer. function claimOwnership() public onlyPendingOwner { emit OwnershipTransferred(owner, pendingOwner); owner = pendingOwner; pendingOwner = address(0); } } // File: contracts/core/iface/IBlockVerifier.sol // Copyright 2017 Loopring Technology Limited. /// @title IBlockVerifier /// @author Brecht Devos - <[email protected]> abstract contract IBlockVerifier is Claimable { // -- Events -- event CircuitRegistered( uint8 indexed blockType, uint16 blockSize, uint8 blockVersion ); event CircuitDisabled( uint8 indexed blockType, uint16 blockSize, uint8 blockVersion ); // -- Public functions -- /// @dev Sets the verifying key for the specified circuit. /// Every block permutation needs its own circuit and thus its own set of /// verification keys. Only a limited number of block sizes per block /// type are supported. /// @param blockType The type of the block /// @param blockSize The number of requests handled in the block /// @param blockVersion The block version (i.e. which circuit version needs to be used) /// @param vk The verification key function registerCircuit( uint8 blockType, uint16 blockSize, uint8 blockVersion, uint[18] calldata vk ) external virtual; /// @dev Disables the use of the specified circuit. /// @param blockType The type of the block /// @param blockSize The number of requests handled in the block /// @param blockVersion The block version (i.e. which circuit version needs to be used) function disableCircuit( uint8 blockType, uint16 blockSize, uint8 blockVersion ) external virtual; /// @dev Verifies blocks with the given public data and proofs. /// Verifying a block makes sure all requests handled in the block /// are correctly handled by the operator. /// @param blockType The type of block /// @param blockSize The number of requests handled in the block /// @param blockVersion The block version (i.e. which circuit version needs to be used) /// @param publicInputs The hash of all the public data of the blocks /// @param proofs The ZK proofs proving that the blocks are correct /// @return True if the block is valid, false otherwise function verifyProofs( uint8 blockType, uint16 blockSize, uint8 blockVersion, uint[] calldata publicInputs, uint[] calldata proofs ) external virtual view returns (bool); /// @dev Checks if a circuit with the specified parameters is registered. /// @param blockType The type of the block /// @param blockSize The number of requests handled in the block /// @param blockVersion The block version (i.e. which circuit version needs to be used) /// @return True if the circuit is registered, false otherwise function isCircuitRegistered( uint8 blockType, uint16 blockSize, uint8 blockVersion ) external virtual view returns (bool); /// @dev Checks if a circuit can still be used to commit new blocks. /// @param blockType The type of the block /// @param blockSize The number of requests handled in the block /// @param blockVersion The block version (i.e. which circuit version needs to be used) /// @return True if the circuit is enabled, false otherwise function isCircuitEnabled( uint8 blockType, uint16 blockSize, uint8 blockVersion ) external virtual view returns (bool); } // File: contracts/core/iface/IDepositContract.sol // Copyright 2017 Loopring Technology Limited. /// @title IDepositContract. /// @dev Contract storing and transferring funds for an exchange. /// /// ERC1155 tokens can be supported by registering pseudo token addresses calculated /// as `address(keccak256(real_token_address, token_params))`. Then the custom /// deposit contract can look up the real token address and paramsters with the /// pseudo token address before doing the transfers. /// @author Brecht Devos - <[email protected]> interface IDepositContract { /// @dev Returns if a token is suppoprted by this contract. function isTokenSupported(address token) external view returns (bool); /// @dev Transfers tokens from a user to the exchange. This function will /// be called when a user deposits funds to the exchange. /// In a simple implementation the funds are simply stored inside the /// deposit contract directly. More advanced implementations may store the funds /// in some DeFi application to earn interest, so this function could directly /// call the necessary functions to store the funds there. /// /// This function needs to throw when an error occurred! /// /// This function can only be called by the exchange. /// /// @param from The address of the account that sends the tokens. /// @param token The address of the token to transfer (`0x0` for ETH). /// @param amount The amount of tokens to transfer. /// @param extraData Opaque data that can be used by the contract to handle the deposit /// @return amountReceived The amount to deposit to the user's account in the Merkle tree function deposit( address from, address token, uint96 amount, bytes calldata extraData ) external payable returns (uint96 amountReceived); /// @dev Transfers tokens from the exchange to a user. This function will /// be called when a withdrawal is done for a user on the exchange. /// In the simplest implementation the funds are simply stored inside the /// deposit contract directly so this simply transfers the requested tokens back /// to the user. More advanced implementations may store the funds /// in some DeFi application to earn interest so the function would /// need to get those tokens back from the DeFi application first before they /// can be transferred to the user. /// /// This function needs to throw when an error occurred! /// /// This function can only be called by the exchange. /// /// @param from The address from which 'amount' tokens are transferred. /// @param to The address to which 'amount' tokens are transferred. /// @param token The address of the token to transfer (`0x0` for ETH). /// @param amount The amount of tokens transferred. /// @param extraData Opaque data that can be used by the contract to handle the withdrawal function withdraw( address from, address to, address token, uint amount, bytes calldata extraData ) external payable; /// @dev Transfers tokens (ETH not supported) for a user using the allowance set /// for the exchange. This way the approval can be used for all functionality (and /// extended functionality) of the exchange. /// Should NOT be used to deposit/withdraw user funds, `deposit`/`withdraw` /// should be used for that as they will contain specialised logic for those operations. /// This function can be called by the exchange to transfer onchain funds of users /// necessary for Agent functionality. /// /// This function needs to throw when an error occurred! /// /// This function can only be called by the exchange. /// /// @param from The address of the account that sends the tokens. /// @param to The address to which 'amount' tokens are transferred. /// @param token The address of the token to transfer (ETH is and cannot be suppported). /// @param amount The amount of tokens transferred. function transfer( address from, address to, address token, uint amount ) external payable; /// @dev Checks if the given address is used for depositing ETH or not. /// Is used while depositing to send the correct ETH amount to the deposit contract. /// /// Note that 0x0 is always registered for deposting ETH when the exchange is created! /// This function allows additional addresses to be used for depositing ETH, the deposit /// contract can implement different behaviour based on the address value. /// /// @param addr The address to check /// @return True if the address is used for depositing ETH, else false. function isETH(address addr) external view returns (bool); } // File: contracts/core/iface/ILoopringV3.sol // Copyright 2017 Loopring Technology Limited. /// @title ILoopringV3 /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> abstract contract ILoopringV3 is Claimable { // == Events == event ExchangeStakeDeposited(address exchangeAddr, uint amount); event ExchangeStakeWithdrawn(address exchangeAddr, uint amount); event ExchangeStakeBurned(address exchangeAddr, uint amount); event SettingsUpdated(uint time); // == Public Variables == mapping (address => uint) internal exchangeStake; uint public totalStake; address public blockVerifierAddress; uint public forcedWithdrawalFee; uint public tokenRegistrationFeeLRCBase; uint public tokenRegistrationFeeLRCDelta; uint8 public protocolTakerFeeBips; uint8 public protocolMakerFeeBips; address payable public protocolFeeVault; // == Public Functions == /// @dev Returns the LRC token address /// @return the LRC token address function lrcAddress() external view virtual returns (address); /// @dev Updates the global exchange settings. /// This function can only be called by the owner of this contract. /// /// Warning: these new values will be used by existing and /// new Loopring exchanges. function updateSettings( address payable _protocolFeeVault, // address(0) not allowed address _blockVerifierAddress, // address(0) not allowed uint _forcedWithdrawalFee ) external virtual; /// @dev Updates the global protocol fee settings. /// This function can only be called by the owner of this contract. /// /// Warning: these new values will be used by existing and /// new Loopring exchanges. function updateProtocolFeeSettings( uint8 _protocolTakerFeeBips, uint8 _protocolMakerFeeBips ) external virtual; /// @dev Gets the amount of staked LRC for an exchange. /// @param exchangeAddr The address of the exchange /// @return stakedLRC The amount of LRC function getExchangeStake( address exchangeAddr ) public virtual view returns (uint stakedLRC); /// @dev Burns a certain amount of staked LRC for a specific exchange. /// This function is meant to be called only from exchange contracts. /// @return burnedLRC The amount of LRC burned. If the amount is greater than /// the staked amount, all staked LRC will be burned. function burnExchangeStake( uint amount ) external virtual returns (uint burnedLRC); /// @dev Stakes more LRC for an exchange. /// @param exchangeAddr The address of the exchange /// @param amountLRC The amount of LRC to stake /// @return stakedLRC The total amount of LRC staked for the exchange function depositExchangeStake( address exchangeAddr, uint amountLRC ) external virtual returns (uint stakedLRC); /// @dev Withdraws a certain amount of staked LRC for an exchange to the given address. /// This function is meant to be called only from within exchange contracts. /// @param recipient The address to receive LRC /// @param requestedAmount The amount of LRC to withdraw /// @return amountLRC The amount of LRC withdrawn function withdrawExchangeStake( address recipient, uint requestedAmount ) external virtual returns (uint amountLRC); /// @dev Gets the protocol fee values for an exchange. /// @return takerFeeBips The protocol taker fee /// @return makerFeeBips The protocol maker fee function getProtocolFeeValues( ) public virtual view returns ( uint8 takerFeeBips, uint8 makerFeeBips ); } // File: contracts/core/iface/ExchangeData.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeData /// @dev All methods in this lib are internal, therefore, there is no need /// to deploy this library independently. /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library ExchangeData { // -- Enums -- enum TransactionType { NOOP, DEPOSIT, WITHDRAWAL, TRANSFER, SPOT_TRADE, ACCOUNT_UPDATE, AMM_UPDATE, SIGNATURE_VERIFICATION, NFT_MINT, // L2 NFT mint or L1-to-L2 NFT deposit NFT_DATA } enum NftType { ERC1155, ERC721 } // -- Structs -- struct Token { address token; } struct ProtocolFeeData { uint32 syncedAt; // only valid before 2105 (85 years to go) uint8 takerFeeBips; uint8 makerFeeBips; uint8 previousTakerFeeBips; uint8 previousMakerFeeBips; } // General auxiliary data for each conditional transaction struct AuxiliaryData { uint txIndex; bool approved; bytes data; } // This is the (virtual) block the owner needs to submit onchain to maintain the // per-exchange (virtual) blockchain. struct Block { uint8 blockType; uint16 blockSize; uint8 blockVersion; bytes data; uint256[8] proof; // Whether we should store the @BlockInfo for this block on-chain. bool storeBlockInfoOnchain; // Block specific data that is only used to help process the block on-chain. // It is not used as input for the circuits and it is not necessary for data-availability. // This bytes array contains the abi encoded AuxiliaryData[] data. bytes auxiliaryData; // Arbitrary data, mainly for off-chain data-availability, i.e., // the multihash of the IPFS file that contains the block data. bytes offchainData; } struct BlockInfo { // The time the block was submitted on-chain. uint32 timestamp; // The public data hash of the block (the 28 most significant bytes). bytes28 blockDataHash; } // Represents an onchain deposit request. struct Deposit { uint96 amount; uint64 timestamp; } // A forced withdrawal request. // If the actual owner of the account initiated the request (we don't know who the owner is // at the time the request is being made) the full balance will be withdrawn. struct ForcedWithdrawal { address owner; uint64 timestamp; } struct Constants { uint SNARK_SCALAR_FIELD; uint MAX_OPEN_FORCED_REQUESTS; uint MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE; uint TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS; uint MAX_NUM_ACCOUNTS; uint MAX_NUM_TOKENS; uint MIN_AGE_PROTOCOL_FEES_UNTIL_UPDATED; uint MIN_TIME_IN_SHUTDOWN; uint TX_DATA_AVAILABILITY_SIZE; uint MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE_UPPERBOUND; } // This is the prime number that is used for the alt_bn128 elliptic curve, see EIP-196. uint public constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; uint public constant MAX_OPEN_FORCED_REQUESTS = 4096; uint public constant MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE = 15 days; uint public constant TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS = 7 days; uint public constant MAX_NUM_ACCOUNTS = 2 ** 32; uint public constant MAX_NUM_TOKENS = 2 ** 16; uint public constant MIN_AGE_PROTOCOL_FEES_UNTIL_UPDATED = 7 days; uint public constant MIN_TIME_IN_SHUTDOWN = 30 days; // The amount of bytes each rollup transaction uses in the block data for data-availability. // This is the maximum amount of bytes of all different transaction types. uint32 public constant MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE_UPPERBOUND = 15 days; uint32 public constant ACCOUNTID_PROTOCOLFEE = 0; uint public constant TX_DATA_AVAILABILITY_SIZE = 68; uint public constant TX_DATA_AVAILABILITY_SIZE_PART_1 = 29; uint public constant TX_DATA_AVAILABILITY_SIZE_PART_2 = 39; uint public constant NFT_TOKEN_ID_START = 2 ** 15; struct AccountLeaf { uint32 accountID; address owner; uint pubKeyX; uint pubKeyY; uint32 nonce; uint feeBipsAMM; } struct BalanceLeaf { uint16 tokenID; uint96 balance; uint weightAMM; uint storageRoot; } struct Nft { address minter; // Minter address for a L2 mint or // the NFT's contract address in the case of a L1-to-L2 NFT deposit. NftType nftType; address token; uint256 nftID; uint8 creatorFeeBips; } struct MerkleProof { ExchangeData.AccountLeaf accountLeaf; ExchangeData.BalanceLeaf balanceLeaf; ExchangeData.Nft nft; uint[48] accountMerkleProof; uint[24] balanceMerkleProof; } struct BlockContext { bytes32 DOMAIN_SEPARATOR; uint32 timestamp; Block block; uint txIndex; } // Represents the entire exchange state except the owner of the exchange. struct State { uint32 maxAgeDepositUntilWithdrawable; bytes32 DOMAIN_SEPARATOR; ILoopringV3 loopring; IBlockVerifier blockVerifier; IAgentRegistry agentRegistry; IDepositContract depositContract; // The merkle root of the offchain data stored in a Merkle tree. The Merkle tree // stores balances for users using an account model. bytes32 merkleRoot; // List of all blocks mapping(uint => BlockInfo) blocks; uint numBlocks; // List of all tokens Token[] tokens; // A map from a token to its tokenID + 1 mapping (address => uint16) tokenToTokenId; // A map from an accountID to a tokenID to if the balance is withdrawn mapping (uint32 => mapping (uint16 => bool)) withdrawnInWithdrawMode; // A map from an account to a token to the amount withdrawable for that account. // This is only used when the automatic distribution of the withdrawal failed. mapping (address => mapping (uint16 => uint)) amountWithdrawable; // A map from an account to a token to the forced withdrawal (always full balance) // The `uint16' represents ERC20 token ID (if < NFT_TOKEN_ID_START) or // NFT balance slot (if >= NFT_TOKEN_ID_START) mapping (uint32 => mapping (uint16 => ForcedWithdrawal)) pendingForcedWithdrawals; // A map from an address to a token to a deposit mapping (address => mapping (uint16 => Deposit)) pendingDeposits; // A map from an account owner to an approved transaction hash to if the transaction is approved or not mapping (address => mapping (bytes32 => bool)) approvedTx; // A map from an account owner to a destination address to a tokenID to an amount to a storageID to a new recipient address mapping (address => mapping (address => mapping (uint16 => mapping (uint => mapping (uint32 => address))))) withdrawalRecipient; // Counter to keep track of how many of forced requests are open so we can limit the work that needs to be done by the owner uint32 numPendingForcedTransactions; // Cached data for the protocol fee ProtocolFeeData protocolFeeData; // Time when the exchange was shutdown uint shutdownModeStartTime; // Time when the exchange has entered withdrawal mode uint withdrawalModeStartTime; // Last time the protocol fee was withdrawn for a specific token mapping (address => uint) protocolFeeLastWithdrawnTime; // Duplicated loopring address address loopringAddr; // AMM fee bips uint8 ammFeeBips; // Enable/Disable `onchainTransferFrom` bool allowOnchainTransferFrom; // owner => NFT type => token address => nftID => Deposit mapping (address => mapping (NftType => mapping (address => mapping(uint256 => Deposit)))) pendingNFTDeposits; // owner => minter => NFT type => token address => nftID => amount withdrawable // This is only used when the automatic distribution of the withdrawal failed. mapping (address => mapping (address => mapping (NftType => mapping (address => mapping(uint256 => uint))))) amountWithdrawableNFT; } } // File: contracts/core/iface/IExchangeV3.sol // Copyright 2017 Loopring Technology Limited. /// @title IExchangeV3 /// @dev Note that Claimable and RentrancyGuard are inherited here to /// ensure all data members are declared on IExchangeV3 to make it /// easy to support upgradability through proxies. /// /// Subclasses of this contract must NOT define constructor to /// initialize data. /// /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> abstract contract IExchangeV3 is Claimable { // -- Events -- event ExchangeCloned( address exchangeAddress, address owner, bytes32 genesisMerkleRoot ); event TokenRegistered( address token, uint16 tokenId ); event Shutdown( uint timestamp ); event WithdrawalModeActivated( uint timestamp ); event BlockSubmitted( uint indexed blockIdx, bytes32 merkleRoot, bytes32 publicDataHash ); event DepositRequested( address from, address to, address token, uint16 tokenId, uint96 amount ); event NFTDepositRequested( address from, address to, uint8 nftType, address token, uint256 nftID, uint96 amount ); event ForcedWithdrawalRequested( address owner, uint16 tokenID, uint32 accountID ); event WithdrawalCompleted( uint8 category, address from, address to, address token, uint amount ); event WithdrawalFailed( uint8 category, address from, address to, address token, uint amount ); event NftWithdrawalCompleted( uint8 category, address from, address to, uint16 tokenID, address token, uint256 nftID, uint amount ); event NftWithdrawalFailed( uint8 category, address from, address to, uint16 tokenID, address token, uint256 nftID, uint amount ); event ProtocolFeesUpdated( uint8 takerFeeBips, uint8 makerFeeBips, uint8 previousTakerFeeBips, uint8 previousMakerFeeBips ); event TransactionApproved( address owner, bytes32 transactionHash ); // -- Initialization -- /// @dev Initializes this exchange. This method can only be called once. /// @param loopring The LoopringV3 contract address. /// @param owner The owner of this exchange. /// @param genesisMerkleRoot The initial Merkle tree state. function initialize( address loopring, address owner, bytes32 genesisMerkleRoot ) virtual external; /// @dev Initialized the agent registry contract used by the exchange. /// Can only be called by the exchange owner once. /// @param agentRegistry The agent registry contract to be used function setAgentRegistry(address agentRegistry) external virtual; /// @dev Gets the agent registry contract used by the exchange. /// @return the agent registry contract function getAgentRegistry() external virtual view returns (IAgentRegistry); /// Can only be called by the exchange owner once. /// @param depositContract The deposit contract to be used function setDepositContract(address depositContract) external virtual; /// @dev refresh the blockVerifier contract which maybe changed in loopringV3 contract. function refreshBlockVerifier() external virtual; /// @dev Gets the deposit contract used by the exchange. /// @return the deposit contract function getDepositContract() external virtual view returns (IDepositContract); // @dev Exchange owner withdraws fees from the exchange. // @param token Fee token address // @param feeRecipient Fee recipient address function withdrawExchangeFees( address token, address feeRecipient ) external virtual; // -- Constants -- /// @dev Returns a list of constants used by the exchange. /// @return constants The list of constants. function getConstants() external virtual pure returns(ExchangeData.Constants memory); // -- Mode -- /// @dev Returns hether the exchange is in withdrawal mode. /// @return Returns true if the exchange is in withdrawal mode, else false. function isInWithdrawalMode() external virtual view returns (bool); /// @dev Returns whether the exchange is shutdown. /// @return Returns true if the exchange is shutdown, else false. function isShutdown() external virtual view returns (bool); // -- Tokens -- /// @dev Registers an ERC20 token for a token id. Note that different exchanges may have /// different ids for the same ERC20 token. /// /// Please note that 1 is reserved for Ether (ETH), 2 is reserved for Wrapped Ether (ETH), /// and 3 is reserved for Loopring Token (LRC). /// /// This function is only callable by the exchange owner. /// /// @param tokenAddress The token's address /// @return tokenID The token's ID in this exchanges. function registerToken( address tokenAddress ) external virtual returns (uint16 tokenID); /// @dev Returns the id of a registered token. /// @param tokenAddress The token's address /// @return tokenID The token's ID in this exchanges. function getTokenID( address tokenAddress ) external virtual view returns (uint16 tokenID); /// @dev Returns the address of a registered token. /// @param tokenID The token's ID in this exchanges. /// @return tokenAddress The token's address function getTokenAddress( uint16 tokenID ) external virtual view returns (address tokenAddress); // -- Stakes -- /// @dev Gets the amount of LRC the owner has staked onchain for this exchange. /// The stake will be burned if the exchange does not fulfill its duty by /// processing user requests in time. Please note that order matching may potentially /// performed by another party and is not part of the exchange's duty. /// /// @return The amount of LRC staked function getExchangeStake() external virtual view returns (uint); /// @dev Withdraws the amount staked for this exchange. /// This can only be done if the exchange has been correctly shutdown: /// - The exchange owner has shutdown the exchange /// - All deposit requests are processed /// - All funds are returned to the users (merkle root is reset to initial state) /// /// Can only be called by the exchange owner. /// /// @return amountLRC The amount of LRC withdrawn function withdrawExchangeStake( address recipient ) external virtual returns (uint amountLRC); /// @dev Can by called by anyone to burn the stake of the exchange when certain /// conditions are fulfilled. /// /// Currently this will only burn the stake of the exchange if /// the exchange is in withdrawal mode. function burnExchangeStake() external virtual; // -- Blocks -- /// @dev Gets the current Merkle root of this exchange's virtual blockchain. /// @return The current Merkle root. function getMerkleRoot() external virtual view returns (bytes32); /// @dev Gets the height of this exchange's virtual blockchain. The block height for a /// new exchange is 1. /// @return The virtual blockchain height which is the index of the last block. function getBlockHeight() external virtual view returns (uint); /// @dev Gets some minimal info of a previously submitted block that's kept onchain. /// A DEX can use this function to implement a payment receipt verification /// contract with a challange-response scheme. /// @param blockIdx The block index. function getBlockInfo(uint blockIdx) external virtual view returns (ExchangeData.BlockInfo memory); /// @dev Sumbits new blocks to the rollup blockchain. /// /// This function can only be called by the exchange operator. /// /// @param blocks The blocks being submitted /// - blockType: The type of the new block /// - blockSize: The number of onchain or offchain requests/settlements /// that have been processed in this block /// - blockVersion: The circuit version to use for verifying the block /// - storeBlockInfoOnchain: If the block info for this block needs to be stored on-chain /// - data: The data for this block /// - offchainData: Arbitrary data, mainly for off-chain data-availability, i.e., /// the multihash of the IPFS file that contains the block data. function submitBlocks(ExchangeData.Block[] calldata blocks) external virtual; /// @dev Gets the number of available forced request slots. /// @return The number of available slots. function getNumAvailableForcedSlots() external virtual view returns (uint); // -- Deposits -- /// @dev Deposits Ether or ERC20 tokens to the specified account. /// /// This function is only callable by an agent of 'from'. /// /// The operator is not forced to do the deposit. /// /// @param from The address that deposits the funds to the exchange /// @param to The account owner's address receiving the funds /// @param tokenAddress The address of the token, use `0x0` for Ether. /// @param amount The amount of tokens to deposit /// @param extraData Optional extra data used by the deposit contract function deposit( address from, address to, address tokenAddress, uint96 amount, bytes calldata extraData ) external virtual payable; /// @dev Deposits an NFT to the specified account. /// /// This function is only callable by an agent of 'from'. /// /// The operator is not forced to do the deposit. /// /// @param from The address that deposits the funds to the exchange /// @param to The account owner's address receiving the funds /// @param nftType The type of NFT contract address (ERC721/ERC1155/...) /// @param tokenAddress The address of the token, use `0x0` for Ether. /// @param nftID The token type 'id`. /// @param amount The amount of tokens to deposit. /// @param extraData Optional extra data used by the deposit contract. function depositNFT( address from, address to, ExchangeData.NftType nftType, address tokenAddress, uint256 nftID, uint96 amount, bytes calldata extraData ) external virtual; /// @dev Gets the amount of tokens that may be added to the owner's account. /// @param owner The destination address for the amount deposited. /// @param tokenAddress The address of the token, use `0x0` for Ether. /// @return The amount of tokens pending. function getPendingDepositAmount( address owner, address tokenAddress ) public virtual view returns (uint96); /// @dev Gets the amount of tokens that may be added to the owner's account. /// @param owner The destination address for the amount deposited. /// @param tokenAddress The address of the token, use `0x0` for Ether. /// @param nftType The type of NFT contract address (ERC721/ERC1155/...) /// @param nftID The token type 'id`. /// @return The amount of tokens pending. function getPendingNFTDepositAmount( address owner, address tokenAddress, ExchangeData.NftType nftType, uint256 nftID ) public virtual view returns (uint96); // -- Withdrawals -- /// @dev Submits an onchain request to force withdraw Ether, ERC20 and NFT tokens. /// This request always withdraws the full balance. /// /// This function is only callable by an agent of the account. /// /// The total fee in ETH that the user needs to pay is 'withdrawalFee'. /// If the user sends too much ETH the surplus is sent back immediately. /// /// Note that after such an operation, it will take the owner some /// time (no more than MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE) to process the request /// and create the deposit to the offchain account. /// /// @param owner The expected owner of the account /// @param tokenID The tokenID to withdraw from /// @param accountID The address the account in the Merkle tree. function forceWithdrawByTokenID( address owner, uint16 tokenID, uint32 accountID ) external virtual payable; /// @dev Submits an onchain request to force withdraw Ether or ERC20 tokens. /// This request always withdraws the full balance. /// /// This function is only callable by an agent of the account. /// /// The total fee in ETH that the user needs to pay is 'withdrawalFee'. /// If the user sends too much ETH the surplus is sent back immediately. /// /// Note that after such an operation, it will take the owner some /// time (no more than MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE) to process the request /// and create the deposit to the offchain account. /// /// @param owner The expected owner of the account /// @param tokenAddress The address of the token, use `0x0` for Ether. /// @param accountID The address the account in the Merkle tree. function forceWithdraw( address owner, address tokenAddress, uint32 accountID ) external virtual payable; /// @dev Checks if a forced withdrawal is pending for an account balance. /// @param accountID The accountID of the account to check. /// @param token The token address /// @return True if a request is pending, false otherwise function isForcedWithdrawalPending( uint32 accountID, address token ) external virtual view returns (bool); /// @dev Submits an onchain request to withdraw Ether or ERC20 tokens from the /// protocol fees account. The complete balance is always withdrawn. /// /// Anyone can request a withdrawal of the protocol fees. /// /// Note that after such an operation, it will take the owner some /// time (no more than MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE) to process the request /// and create the deposit to the offchain account. /// /// @param tokenAddress The address of the token, use `0x0` for Ether. function withdrawProtocolFees( address tokenAddress ) external virtual payable; /// @dev Gets the time the protocol fee for a token was last withdrawn. /// @param tokenAddress The address of the token, use `0x0` for Ether. /// @return The time the protocol fee was last withdrawn. function getProtocolFeeLastWithdrawnTime( address tokenAddress ) external virtual view returns (uint); /// @dev Allows anyone to withdraw funds for a specified user using the balances stored /// in the Merkle tree. The funds will be sent to the owner of the acount. /// /// Can only be used in withdrawal mode (i.e. when the owner has stopped /// committing blocks and is not able to commit any more blocks). /// /// This will NOT modify the onchain merkle root! The merkle root stored /// onchain will remain the same after the withdrawal. We store if the user /// has withdrawn the balance in State.withdrawnInWithdrawMode. /// /// @param merkleProof The Merkle inclusion proof function withdrawFromMerkleTree( ExchangeData.MerkleProof calldata merkleProof ) external virtual; /// @dev Checks if the balance for the account was withdrawn with `withdrawFromMerkleTree`. /// @param accountID The accountID of the balance to check. /// @param token The token address /// @return True if it was already withdrawn, false otherwise function isWithdrawnInWithdrawalMode( uint32 accountID, address token ) external virtual view returns (bool); /// @dev Allows withdrawing funds deposited to the contract in a deposit request when /// it was never processed by the owner within the maximum time allowed. /// /// Can be called by anyone. The deposited tokens will be sent back to /// the owner of the account they were deposited in. /// /// @param owner The address of the account the withdrawal was done for. /// @param token The token address function withdrawFromDepositRequest( address owner, address token ) external virtual; /// @dev Allows withdrawing funds deposited to the contract in a deposit request when /// it was never processed by the owner within the maximum time allowed. /// /// Can be called by anyone. The deposited tokens will be sent back to /// the owner of the account they were deposited in. /// /// @param owner The address of the account the withdrawal was done for. /// @param token The token address /// @param nftType The type of NFT contract address (ERC721/ERC1155/...) /// @param nftID The token type 'id`. function withdrawFromNFTDepositRequest( address owner, address token, ExchangeData.NftType nftType, uint256 nftID ) external virtual; /// @dev Allows withdrawing funds after a withdrawal request (either onchain /// or offchain) was submitted in a block by the operator. /// /// Can be called by anyone. The withdrawn tokens will be sent to /// the owner of the account they were withdrawn out. /// /// Normally it is should not be needed for users to call this manually. /// Funds from withdrawal requests will be sent to the account owner /// immediately by the owner when the block is submitted. /// The user will however need to call this manually if the transfer failed. /// /// Tokens and owners must have the same size. /// /// @param owners The addresses of the account the withdrawal was done for. /// @param tokens The token addresses function withdrawFromApprovedWithdrawals( address[] calldata owners, address[] calldata tokens ) external virtual; /// @dev Allows withdrawing funds after an NFT withdrawal request (either onchain /// or offchain) was submitted in a block by the operator. /// /// Can be called by anyone. The withdrawn tokens will be sent to /// the owner of the account they were withdrawn out. /// /// Normally it is should not be needed for users to call this manually. /// Funds from withdrawal requests will be sent to the account owner /// immediately by the owner when the block is submitted. /// The user will however need to call this manually if the transfer failed. /// /// All input arrays must have the same size. /// /// @param owners The addresses of the accounts the withdrawal was done for. /// @param minters The addresses of the minters. /// @param nftTypes The NFT token addresses types /// @param tokens The token addresses /// @param nftIDs The token ids function withdrawFromApprovedWithdrawalsNFT( address[] memory owners, address[] memory minters, ExchangeData.NftType[] memory nftTypes, address[] memory tokens, uint256[] memory nftIDs ) external virtual; /// @dev Gets the amount that can be withdrawn immediately with `withdrawFromApprovedWithdrawals`. /// @param owner The address of the account the withdrawal was done for. /// @param token The token address /// @return The amount withdrawable function getAmountWithdrawable( address owner, address token ) external virtual view returns (uint); /// @dev Gets the amount that can be withdrawn immediately with `withdrawFromApprovedWithdrawalsNFT`. /// @param owner The address of the account the withdrawal was done for. /// @param token The token address /// @param nftType The NFT token address types /// @param nftID The token id /// @param minter The NFT minter /// @return The amount withdrawable function getAmountWithdrawableNFT( address owner, address token, ExchangeData.NftType nftType, uint256 nftID, address minter ) external virtual view returns (uint); /// @dev Notifies the exchange that the owner did not process a forced request. /// If this is indeed the case, the exchange will enter withdrawal mode. /// /// Can be called by anyone. /// /// @param accountID The accountID the forced request was made for /// @param tokenID The tokenID of the the forced request function notifyForcedRequestTooOld( uint32 accountID, uint16 tokenID ) external virtual; /// @dev Allows a withdrawal to be done to an adddresss that is different /// than initialy specified in the withdrawal request. This can be used to /// implement functionality like fast withdrawals. /// /// This function can only be called by an agent. /// /// @param from The address of the account that does the withdrawal. /// @param to The address to which 'amount' tokens were going to be withdrawn. /// @param token The address of the token that is withdrawn ('0x0' for ETH). /// @param amount The amount of tokens that are going to be withdrawn. /// @param storageID The storageID of the withdrawal request. /// @param newRecipient The new recipient address of the withdrawal. function setWithdrawalRecipient( address from, address to, address token, uint96 amount, uint32 storageID, address newRecipient ) external virtual; /// @dev Gets the withdrawal recipient. /// /// @param from The address of the account that does the withdrawal. /// @param to The address to which 'amount' tokens were going to be withdrawn. /// @param token The address of the token that is withdrawn ('0x0' for ETH). /// @param amount The amount of tokens that are going to be withdrawn. /// @param storageID The storageID of the withdrawal request. function getWithdrawalRecipient( address from, address to, address token, uint96 amount, uint32 storageID ) external virtual view returns (address); /// @dev Allows an agent to transfer ERC-20 tokens for a user using the allowance /// the user has set for the exchange. This way the user only needs to approve a single exchange contract /// for all exchange/agent features, which allows for a more seamless user experience. /// /// This function can only be called by an agent. /// /// @param from The address of the account that sends the tokens. /// @param to The address to which 'amount' tokens are transferred. /// @param token The address of the token to transfer (ETH is and cannot be suppported). /// @param amount The amount of tokens transferred. function onchainTransferFrom( address from, address to, address token, uint amount ) external virtual; /// @dev Allows an agent to approve a rollup tx. /// /// This function can only be called by an agent. /// /// @param owner The owner of the account /// @param txHash The hash of the transaction function approveTransaction( address owner, bytes32 txHash ) external virtual; /// @dev Allows an agent to approve multiple rollup txs. /// /// This function can only be called by an agent. /// /// @param owners The account owners /// @param txHashes The hashes of the transactions function approveTransactions( address[] calldata owners, bytes32[] calldata txHashes ) external virtual; /// @dev Checks if a rollup tx is approved using the tx's hash. /// /// @param owner The owner of the account that needs to authorize the tx /// @param txHash The hash of the transaction /// @return True if the tx is approved, else false function isTransactionApproved( address owner, bytes32 txHash ) external virtual view returns (bool); // -- Admins -- /// @dev Sets the max time deposits have to wait before becoming withdrawable. /// @param newValue The new value. /// @return The old value. function setMaxAgeDepositUntilWithdrawable( uint32 newValue ) external virtual returns (uint32); /// @dev Returns the max time deposits have to wait before becoming withdrawable. /// @return The value. function getMaxAgeDepositUntilWithdrawable() external virtual view returns (uint32); /// @dev Shuts down the exchange. /// Once the exchange is shutdown all onchain requests are permanently disabled. /// When all requirements are fulfilled the exchange owner can withdraw /// the exchange stake with withdrawStake. /// /// Note that the exchange can still enter the withdrawal mode after this function /// has been invoked successfully. To prevent entering the withdrawal mode before the /// the echange stake can be withdrawn, all withdrawal requests still need to be handled /// for at least MIN_TIME_IN_SHUTDOWN seconds. /// /// Can only be called by the exchange owner. /// /// @return success True if the exchange is shutdown, else False function shutdown() external virtual returns (bool success); /// @dev Gets the protocol fees for this exchange. /// @return syncedAt The timestamp the protocol fees were last updated /// @return takerFeeBips The protocol taker fee /// @return makerFeeBips The protocol maker fee /// @return previousTakerFeeBips The previous protocol taker fee /// @return previousMakerFeeBips The previous protocol maker fee function getProtocolFeeValues() external virtual view returns ( uint32 syncedAt, uint8 takerFeeBips, uint8 makerFeeBips, uint8 previousTakerFeeBips, uint8 previousMakerFeeBips ); /// @dev Gets the domain separator used in this exchange. function getDomainSeparator() external virtual view returns (bytes32); /// @dev set amm pool feeBips value. function setAmmFeeBips(uint8 _feeBips) external virtual; /// @dev get amm pool feeBips value. function getAmmFeeBips() external virtual view returns (uint8); } // File: contracts/lib/ERC20.sol // Copyright 2017 Loopring Technology Limited. /// @title ERC20 Token Interface /// @dev see https://github.com/ethereum/EIPs/issues/20 /// @author Daniel Wang - <[email protected]> abstract contract ERC20 { function totalSupply() public virtual view returns (uint); function balanceOf( address who ) public virtual view returns (uint); function allowance( address owner, address spender ) public virtual view returns (uint); function transfer( address to, uint value ) public virtual returns (bool); function transferFrom( address from, address to, uint value ) public virtual returns (bool); function approve( address spender, uint value ) public virtual returns (bool); } // File: contracts/core/impl/libexchange/ExchangeMode.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeMode. /// @dev All methods in this lib are internal, therefore, there is no need /// to deploy this library independently. /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> library ExchangeMode { using MathUint for uint; function isInWithdrawalMode( ExchangeData.State storage S ) internal // inline call view returns (bool result) { result = S.withdrawalModeStartTime > 0; } function isShutdown( ExchangeData.State storage S ) internal // inline call view returns (bool) { return S.shutdownModeStartTime > 0; } function getNumAvailableForcedSlots( ExchangeData.State storage S ) internal view returns (uint) { return ExchangeData.MAX_OPEN_FORCED_REQUESTS - S.numPendingForcedTransactions; } } // File: contracts/core/impl/libexchange/ExchangeAdmins.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeAdmins. /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library ExchangeAdmins { using ERC20SafeTransfer for address; using ExchangeMode for ExchangeData.State; using MathUint for uint; event MaxAgeDepositUntilWithdrawableChanged( address indexed exchangeAddr, uint32 oldValue, uint32 newValue ); function setMaxAgeDepositUntilWithdrawable( ExchangeData.State storage S, uint32 newValue ) public returns (uint32 oldValue) { require(!S.isInWithdrawalMode(), "INVALID_MODE"); require( newValue > 0 && newValue <= ExchangeData.MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE_UPPERBOUND, "INVALID_VALUE" ); oldValue = S.maxAgeDepositUntilWithdrawable; S.maxAgeDepositUntilWithdrawable = newValue; emit MaxAgeDepositUntilWithdrawableChanged( address(this), oldValue, newValue ); } function withdrawExchangeStake( ExchangeData.State storage S, address recipient ) public returns (uint) { // Exchange needs to be shutdown require(S.isShutdown(), "EXCHANGE_NOT_SHUTDOWN"); require(!S.isInWithdrawalMode(), "CANNOT_BE_IN_WITHDRAWAL_MODE"); // Need to remain in shutdown for some time require(block.timestamp >= S.shutdownModeStartTime + ExchangeData.MIN_TIME_IN_SHUTDOWN, "TOO_EARLY"); // Withdraw the complete stake uint amount = S.loopring.getExchangeStake(address(this)); return S.loopring.withdrawExchangeStake(recipient, amount); } } // File: contracts/lib/Poseidon.sol // Copyright 2017 Loopring Technology Limited. /// @title Poseidon hash function /// See: https://eprint.iacr.org/2019/458.pdf /// Code auto-generated by generate_poseidon_EVM_code.py /// @author Brecht Devos - <[email protected]> library Poseidon { // // hash_t4f6p52 // struct HashInputs4 { uint t0; uint t1; uint t2; uint t3; } function mix(HashInputs4 memory i, uint q) internal pure { HashInputs4 memory o; o.t0 = mulmod(i.t0, 11739432287187184656569880828944421268616385874806221589758215824904320817117, q); o.t0 = addmod(o.t0, mulmod(i.t1, 4977258759536702998522229302103997878600602264560359702680165243908162277980, q), q); o.t0 = addmod(o.t0, mulmod(i.t2, 19167410339349846567561662441069598364702008768579734801591448511131028229281, q), q); o.t0 = addmod(o.t0, mulmod(i.t3, 14183033936038168803360723133013092560869148726790180682363054735190196956789, q), q); o.t1 = mulmod(i.t0, 16872301185549870956030057498946148102848662396374401407323436343924021192350, q); o.t1 = addmod(o.t1, mulmod(i.t1, 107933704346764130067829474107909495889716688591997879426350582457782826785, q), q); o.t1 = addmod(o.t1, mulmod(i.t2, 17034139127218860091985397764514160131253018178110701196935786874261236172431, q), q); o.t1 = addmod(o.t1, mulmod(i.t3, 2799255644797227968811798608332314218966179365168250111693473252876996230317, q), q); o.t2 = mulmod(i.t0, 18618317300596756144100783409915332163189452886691331959651778092154775572832, q); o.t2 = addmod(o.t2, mulmod(i.t1, 13596762909635538739079656925495736900379091964739248298531655823337482778123, q), q); o.t2 = addmod(o.t2, mulmod(i.t2, 18985203040268814769637347880759846911264240088034262814847924884273017355969, q), q); o.t2 = addmod(o.t2, mulmod(i.t3, 8652975463545710606098548415650457376967119951977109072274595329619335974180, q), q); o.t3 = mulmod(i.t0, 11128168843135959720130031095451763561052380159981718940182755860433840154182, q); o.t3 = addmod(o.t3, mulmod(i.t1, 2953507793609469112222895633455544691298656192015062835263784675891831794974, q), q); o.t3 = addmod(o.t3, mulmod(i.t2, 19025623051770008118343718096455821045904242602531062247152770448380880817517, q), q); o.t3 = addmod(o.t3, mulmod(i.t3, 9077319817220936628089890431129759976815127354480867310384708941479362824016, q), q); i.t0 = o.t0; i.t1 = o.t1; i.t2 = o.t2; i.t3 = o.t3; } function ark(HashInputs4 memory i, uint q, uint c) internal pure { HashInputs4 memory o; o.t0 = addmod(i.t0, c, q); o.t1 = addmod(i.t1, c, q); o.t2 = addmod(i.t2, c, q); o.t3 = addmod(i.t3, c, q); i.t0 = o.t0; i.t1 = o.t1; i.t2 = o.t2; i.t3 = o.t3; } function sbox_full(HashInputs4 memory i, uint q) internal pure { HashInputs4 memory o; o.t0 = mulmod(i.t0, i.t0, q); o.t0 = mulmod(o.t0, o.t0, q); o.t0 = mulmod(i.t0, o.t0, q); o.t1 = mulmod(i.t1, i.t1, q); o.t1 = mulmod(o.t1, o.t1, q); o.t1 = mulmod(i.t1, o.t1, q); o.t2 = mulmod(i.t2, i.t2, q); o.t2 = mulmod(o.t2, o.t2, q); o.t2 = mulmod(i.t2, o.t2, q); o.t3 = mulmod(i.t3, i.t3, q); o.t3 = mulmod(o.t3, o.t3, q); o.t3 = mulmod(i.t3, o.t3, q); i.t0 = o.t0; i.t1 = o.t1; i.t2 = o.t2; i.t3 = o.t3; } function sbox_partial(HashInputs4 memory i, uint q) internal pure { HashInputs4 memory o; o.t0 = mulmod(i.t0, i.t0, q); o.t0 = mulmod(o.t0, o.t0, q); o.t0 = mulmod(i.t0, o.t0, q); i.t0 = o.t0; } function hash_t4f6p52(HashInputs4 memory i, uint q) internal pure returns (uint) { // validate inputs require(i.t0 < q, "INVALID_INPUT"); require(i.t1 < q, "INVALID_INPUT"); require(i.t2 < q, "INVALID_INPUT"); require(i.t3 < q, "INVALID_INPUT"); // round 0 ark(i, q, 14397397413755236225575615486459253198602422701513067526754101844196324375522); sbox_full(i, q); mix(i, q); // round 1 ark(i, q, 10405129301473404666785234951972711717481302463898292859783056520670200613128); sbox_full(i, q); mix(i, q); // round 2 ark(i, q, 5179144822360023508491245509308555580251733042407187134628755730783052214509); sbox_full(i, q); mix(i, q); // round 3 ark(i, q, 9132640374240188374542843306219594180154739721841249568925550236430986592615); sbox_partial(i, q); mix(i, q); // round 4 ark(i, q, 20360807315276763881209958738450444293273549928693737723235350358403012458514); sbox_partial(i, q); mix(i, q); // round 5 ark(i, q, 17933600965499023212689924809448543050840131883187652471064418452962948061619); sbox_partial(i, q); mix(i, q); // round 6 ark(i, q, 3636213416533737411392076250708419981662897009810345015164671602334517041153); sbox_partial(i, q); mix(i, q); // round 7 ark(i, q, 2008540005368330234524962342006691994500273283000229509835662097352946198608); sbox_partial(i, q); mix(i, q); // round 8 ark(i, q, 16018407964853379535338740313053768402596521780991140819786560130595652651567); sbox_partial(i, q); mix(i, q); // round 9 ark(i, q, 20653139667070586705378398435856186172195806027708437373983929336015162186471); sbox_partial(i, q); mix(i, q); // round 10 ark(i, q, 17887713874711369695406927657694993484804203950786446055999405564652412116765); sbox_partial(i, q); mix(i, q); // round 11 ark(i, q, 4852706232225925756777361208698488277369799648067343227630786518486608711772); sbox_partial(i, q); mix(i, q); // round 12 ark(i, q, 8969172011633935669771678412400911310465619639756845342775631896478908389850); sbox_partial(i, q); mix(i, q); // round 13 ark(i, q, 20570199545627577691240476121888846460936245025392381957866134167601058684375); sbox_partial(i, q); mix(i, q); // round 14 ark(i, q, 16442329894745639881165035015179028112772410105963688121820543219662832524136); sbox_partial(i, q); mix(i, q); // round 15 ark(i, q, 20060625627350485876280451423010593928172611031611836167979515653463693899374); sbox_partial(i, q); mix(i, q); // round 16 ark(i, q, 16637282689940520290130302519163090147511023430395200895953984829546679599107); sbox_partial(i, q); mix(i, q); // round 17 ark(i, q, 15599196921909732993082127725908821049411366914683565306060493533569088698214); sbox_partial(i, q); mix(i, q); // round 18 ark(i, q, 16894591341213863947423904025624185991098788054337051624251730868231322135455); sbox_partial(i, q); mix(i, q); // round 19 ark(i, q, 1197934381747032348421303489683932612752526046745577259575778515005162320212); sbox_partial(i, q); mix(i, q); // round 20 ark(i, q, 6172482022646932735745595886795230725225293469762393889050804649558459236626); sbox_partial(i, q); mix(i, q); // round 21 ark(i, q, 21004037394166516054140386756510609698837211370585899203851827276330669555417); sbox_partial(i, q); mix(i, q); // round 22 ark(i, q, 15262034989144652068456967541137853724140836132717012646544737680069032573006); sbox_partial(i, q); mix(i, q); // round 23 ark(i, q, 15017690682054366744270630371095785995296470601172793770224691982518041139766); sbox_partial(i, q); mix(i, q); // round 24 ark(i, q, 15159744167842240513848638419303545693472533086570469712794583342699782519832); sbox_partial(i, q); mix(i, q); // round 25 ark(i, q, 11178069035565459212220861899558526502477231302924961773582350246646450941231); sbox_partial(i, q); mix(i, q); // round 26 ark(i, q, 21154888769130549957415912997229564077486639529994598560737238811887296922114); sbox_partial(i, q); mix(i, q); // round 27 ark(i, q, 20162517328110570500010831422938033120419484532231241180224283481905744633719); sbox_partial(i, q); mix(i, q); // round 28 ark(i, q, 2777362604871784250419758188173029886707024739806641263170345377816177052018); sbox_partial(i, q); mix(i, q); // round 29 ark(i, q, 15732290486829619144634131656503993123618032247178179298922551820261215487562); sbox_partial(i, q); mix(i, q); // round 30 ark(i, q, 6024433414579583476444635447152826813568595303270846875177844482142230009826); sbox_partial(i, q); mix(i, q); // round 31 ark(i, q, 17677827682004946431939402157761289497221048154630238117709539216286149983245); sbox_partial(i, q); mix(i, q); // round 32 ark(i, q, 10716307389353583413755237303156291454109852751296156900963208377067748518748); sbox_partial(i, q); mix(i, q); // round 33 ark(i, q, 14925386988604173087143546225719076187055229908444910452781922028996524347508); sbox_partial(i, q); mix(i, q); // round 34 ark(i, q, 8940878636401797005293482068100797531020505636124892198091491586778667442523); sbox_partial(i, q); mix(i, q); // round 35 ark(i, q, 18911747154199663060505302806894425160044925686870165583944475880789706164410); sbox_partial(i, q); mix(i, q); // round 36 ark(i, q, 8821532432394939099312235292271438180996556457308429936910969094255825456935); sbox_partial(i, q); mix(i, q); // round 37 ark(i, q, 20632576502437623790366878538516326728436616723089049415538037018093616927643); sbox_partial(i, q); mix(i, q); // round 38 ark(i, q, 71447649211767888770311304010816315780740050029903404046389165015534756512); sbox_partial(i, q); mix(i, q); // round 39 ark(i, q, 2781996465394730190470582631099299305677291329609718650018200531245670229393); sbox_partial(i, q); mix(i, q); // round 40 ark(i, q, 12441376330954323535872906380510501637773629931719508864016287320488688345525); sbox_partial(i, q); mix(i, q); // round 41 ark(i, q, 2558302139544901035700544058046419714227464650146159803703499681139469546006); sbox_partial(i, q); mix(i, q); // round 42 ark(i, q, 10087036781939179132584550273563255199577525914374285705149349445480649057058); sbox_partial(i, q); mix(i, q); // round 43 ark(i, q, 4267692623754666261749551533667592242661271409704769363166965280715887854739); sbox_partial(i, q); mix(i, q); // round 44 ark(i, q, 4945579503584457514844595640661884835097077318604083061152997449742124905548); sbox_partial(i, q); mix(i, q); // round 45 ark(i, q, 17742335354489274412669987990603079185096280484072783973732137326144230832311); sbox_partial(i, q); mix(i, q); // round 46 ark(i, q, 6266270088302506215402996795500854910256503071464802875821837403486057988208); sbox_partial(i, q); mix(i, q); // round 47 ark(i, q, 2716062168542520412498610856550519519760063668165561277991771577403400784706); sbox_partial(i, q); mix(i, q); // round 48 ark(i, q, 19118392018538203167410421493487769944462015419023083813301166096764262134232); sbox_partial(i, q); mix(i, q); // round 49 ark(i, q, 9386595745626044000666050847309903206827901310677406022353307960932745699524); sbox_partial(i, q); mix(i, q); // round 50 ark(i, q, 9121640807890366356465620448383131419933298563527245687958865317869840082266); sbox_partial(i, q); mix(i, q); // round 51 ark(i, q, 3078975275808111706229899605611544294904276390490742680006005661017864583210); sbox_partial(i, q); mix(i, q); // round 52 ark(i, q, 7157404299437167354719786626667769956233708887934477609633504801472827442743); sbox_partial(i, q); mix(i, q); // round 53 ark(i, q, 14056248655941725362944552761799461694550787028230120190862133165195793034373); sbox_partial(i, q); mix(i, q); // round 54 ark(i, q, 14124396743304355958915937804966111851843703158171757752158388556919187839849); sbox_partial(i, q); mix(i, q); // round 55 ark(i, q, 11851254356749068692552943732920045260402277343008629727465773766468466181076); sbox_full(i, q); mix(i, q); // round 56 ark(i, q, 9799099446406796696742256539758943483211846559715874347178722060519817626047); sbox_full(i, q); mix(i, q); // round 57 ark(i, q, 10156146186214948683880719664738535455146137901666656566575307300522957959544); sbox_full(i, q); mix(i, q); return i.t0; } // // hash_t5f6p52 // struct HashInputs5 { uint t0; uint t1; uint t2; uint t3; uint t4; } function hash_t5f6p52_internal( uint t0, uint t1, uint t2, uint t3, uint t4, uint q ) internal pure returns (uint) { assembly { function mix(_t0, _t1, _t2, _t3, _t4, _q) -> nt0, nt1, nt2, nt3, nt4 { nt0 := mulmod(_t0, 4977258759536702998522229302103997878600602264560359702680165243908162277980, _q) nt0 := addmod(nt0, mulmod(_t1, 19167410339349846567561662441069598364702008768579734801591448511131028229281, _q), _q) nt0 := addmod(nt0, mulmod(_t2, 14183033936038168803360723133013092560869148726790180682363054735190196956789, _q), _q) nt0 := addmod(nt0, mulmod(_t3, 9067734253445064890734144122526450279189023719890032859456830213166173619761, _q), _q) nt0 := addmod(nt0, mulmod(_t4, 16378664841697311562845443097199265623838619398287411428110917414833007677155, _q), _q) nt1 := mulmod(_t0, 107933704346764130067829474107909495889716688591997879426350582457782826785, _q) nt1 := addmod(nt1, mulmod(_t1, 17034139127218860091985397764514160131253018178110701196935786874261236172431, _q), _q) nt1 := addmod(nt1, mulmod(_t2, 2799255644797227968811798608332314218966179365168250111693473252876996230317, _q), _q) nt1 := addmod(nt1, mulmod(_t3, 2482058150180648511543788012634934806465808146786082148795902594096349483974, _q), _q) nt1 := addmod(nt1, mulmod(_t4, 16563522740626180338295201738437974404892092704059676533096069531044355099628, _q), _q) nt2 := mulmod(_t0, 13596762909635538739079656925495736900379091964739248298531655823337482778123, _q) nt2 := addmod(nt2, mulmod(_t1, 18985203040268814769637347880759846911264240088034262814847924884273017355969, _q), _q) nt2 := addmod(nt2, mulmod(_t2, 8652975463545710606098548415650457376967119951977109072274595329619335974180, _q), _q) nt2 := addmod(nt2, mulmod(_t3, 970943815872417895015626519859542525373809485973005165410533315057253476903, _q), _q) nt2 := addmod(nt2, mulmod(_t4, 19406667490568134101658669326517700199745817783746545889094238643063688871948, _q), _q) nt3 := mulmod(_t0, 2953507793609469112222895633455544691298656192015062835263784675891831794974, _q) nt3 := addmod(nt3, mulmod(_t1, 19025623051770008118343718096455821045904242602531062247152770448380880817517, _q), _q) nt3 := addmod(nt3, mulmod(_t2, 9077319817220936628089890431129759976815127354480867310384708941479362824016, _q), _q) nt3 := addmod(nt3, mulmod(_t3, 4770370314098695913091200576539533727214143013236894216582648993741910829490, _q), _q) nt3 := addmod(nt3, mulmod(_t4, 4298564056297802123194408918029088169104276109138370115401819933600955259473, _q), _q) nt4 := mulmod(_t0, 8336710468787894148066071988103915091676109272951895469087957569358494947747, _q) nt4 := addmod(nt4, mulmod(_t1, 16205238342129310687768799056463408647672389183328001070715567975181364448609, _q), _q) nt4 := addmod(nt4, mulmod(_t2, 8303849270045876854140023508764676765932043944545416856530551331270859502246, _q), _q) nt4 := addmod(nt4, mulmod(_t3, 20218246699596954048529384569730026273241102596326201163062133863539137060414, _q), _q) nt4 := addmod(nt4, mulmod(_t4, 1712845821388089905746651754894206522004527237615042226559791118162382909269, _q), _q) } function ark(_t0, _t1, _t2, _t3, _t4, _q, c) -> nt0, nt1, nt2, nt3, nt4 { nt0 := addmod(_t0, c, _q) nt1 := addmod(_t1, c, _q) nt2 := addmod(_t2, c, _q) nt3 := addmod(_t3, c, _q) nt4 := addmod(_t4, c, _q) } function sbox_full(_t0, _t1, _t2, _t3, _t4, _q) -> nt0, nt1, nt2, nt3, nt4 { nt0 := mulmod(_t0, _t0, _q) nt0 := mulmod(nt0, nt0, _q) nt0 := mulmod(_t0, nt0, _q) nt1 := mulmod(_t1, _t1, _q) nt1 := mulmod(nt1, nt1, _q) nt1 := mulmod(_t1, nt1, _q) nt2 := mulmod(_t2, _t2, _q) nt2 := mulmod(nt2, nt2, _q) nt2 := mulmod(_t2, nt2, _q) nt3 := mulmod(_t3, _t3, _q) nt3 := mulmod(nt3, nt3, _q) nt3 := mulmod(_t3, nt3, _q) nt4 := mulmod(_t4, _t4, _q) nt4 := mulmod(nt4, nt4, _q) nt4 := mulmod(_t4, nt4, _q) } function sbox_partial(_t, _q) -> nt { nt := mulmod(_t, _t, _q) nt := mulmod(nt, nt, _q) nt := mulmod(_t, nt, _q) } // round 0 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 14397397413755236225575615486459253198602422701513067526754101844196324375522) t0, t1, t2, t3, t4 := sbox_full(t0, t1, t2, t3, t4, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 1 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 10405129301473404666785234951972711717481302463898292859783056520670200613128) t0, t1, t2, t3, t4 := sbox_full(t0, t1, t2, t3, t4, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 2 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 5179144822360023508491245509308555580251733042407187134628755730783052214509) t0, t1, t2, t3, t4 := sbox_full(t0, t1, t2, t3, t4, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 3 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 9132640374240188374542843306219594180154739721841249568925550236430986592615) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 4 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 20360807315276763881209958738450444293273549928693737723235350358403012458514) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 5 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 17933600965499023212689924809448543050840131883187652471064418452962948061619) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 6 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 3636213416533737411392076250708419981662897009810345015164671602334517041153) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 7 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 2008540005368330234524962342006691994500273283000229509835662097352946198608) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 8 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 16018407964853379535338740313053768402596521780991140819786560130595652651567) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 9 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 20653139667070586705378398435856186172195806027708437373983929336015162186471) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 10 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 17887713874711369695406927657694993484804203950786446055999405564652412116765) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 11 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 4852706232225925756777361208698488277369799648067343227630786518486608711772) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 12 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 8969172011633935669771678412400911310465619639756845342775631896478908389850) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 13 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 20570199545627577691240476121888846460936245025392381957866134167601058684375) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 14 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 16442329894745639881165035015179028112772410105963688121820543219662832524136) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 15 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 20060625627350485876280451423010593928172611031611836167979515653463693899374) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 16 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 16637282689940520290130302519163090147511023430395200895953984829546679599107) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 17 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 15599196921909732993082127725908821049411366914683565306060493533569088698214) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 18 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 16894591341213863947423904025624185991098788054337051624251730868231322135455) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 19 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 1197934381747032348421303489683932612752526046745577259575778515005162320212) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 20 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 6172482022646932735745595886795230725225293469762393889050804649558459236626) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 21 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 21004037394166516054140386756510609698837211370585899203851827276330669555417) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 22 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 15262034989144652068456967541137853724140836132717012646544737680069032573006) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 23 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 15017690682054366744270630371095785995296470601172793770224691982518041139766) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 24 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 15159744167842240513848638419303545693472533086570469712794583342699782519832) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 25 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 11178069035565459212220861899558526502477231302924961773582350246646450941231) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 26 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 21154888769130549957415912997229564077486639529994598560737238811887296922114) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 27 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 20162517328110570500010831422938033120419484532231241180224283481905744633719) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 28 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 2777362604871784250419758188173029886707024739806641263170345377816177052018) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 29 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 15732290486829619144634131656503993123618032247178179298922551820261215487562) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 30 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 6024433414579583476444635447152826813568595303270846875177844482142230009826) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 31 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 17677827682004946431939402157761289497221048154630238117709539216286149983245) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 32 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 10716307389353583413755237303156291454109852751296156900963208377067748518748) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 33 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 14925386988604173087143546225719076187055229908444910452781922028996524347508) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 34 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 8940878636401797005293482068100797531020505636124892198091491586778667442523) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 35 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 18911747154199663060505302806894425160044925686870165583944475880789706164410) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 36 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 8821532432394939099312235292271438180996556457308429936910969094255825456935) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 37 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 20632576502437623790366878538516326728436616723089049415538037018093616927643) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 38 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 71447649211767888770311304010816315780740050029903404046389165015534756512) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 39 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 2781996465394730190470582631099299305677291329609718650018200531245670229393) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 40 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 12441376330954323535872906380510501637773629931719508864016287320488688345525) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 41 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 2558302139544901035700544058046419714227464650146159803703499681139469546006) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 42 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 10087036781939179132584550273563255199577525914374285705149349445480649057058) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 43 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 4267692623754666261749551533667592242661271409704769363166965280715887854739) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 44 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 4945579503584457514844595640661884835097077318604083061152997449742124905548) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 45 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 17742335354489274412669987990603079185096280484072783973732137326144230832311) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 46 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 6266270088302506215402996795500854910256503071464802875821837403486057988208) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 47 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 2716062168542520412498610856550519519760063668165561277991771577403400784706) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 48 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 19118392018538203167410421493487769944462015419023083813301166096764262134232) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 49 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 9386595745626044000666050847309903206827901310677406022353307960932745699524) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 50 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 9121640807890366356465620448383131419933298563527245687958865317869840082266) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 51 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 3078975275808111706229899605611544294904276390490742680006005661017864583210) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 52 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 7157404299437167354719786626667769956233708887934477609633504801472827442743) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 53 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 14056248655941725362944552761799461694550787028230120190862133165195793034373) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 54 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 14124396743304355958915937804966111851843703158171757752158388556919187839849) t0 := sbox_partial(t0, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 55 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 11851254356749068692552943732920045260402277343008629727465773766468466181076) t0, t1, t2, t3, t4 := sbox_full(t0, t1, t2, t3, t4, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 56 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 9799099446406796696742256539758943483211846559715874347178722060519817626047) t0, t1, t2, t3, t4 := sbox_full(t0, t1, t2, t3, t4, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) // round 57 t0, t1, t2, t3, t4 := ark(t0, t1, t2, t3, t4, q, 10156146186214948683880719664738535455146137901666656566575307300522957959544) t0, t1, t2, t3, t4 := sbox_full(t0, t1, t2, t3, t4, q) t0, t1, t2, t3, t4 := mix(t0, t1, t2, t3, t4, q) } return t0; } function hash_t5f6p52(HashInputs5 memory i, uint q) internal pure returns (uint) { // validate inputs require(i.t0 < q, "INVALID_INPUT"); require(i.t1 < q, "INVALID_INPUT"); require(i.t2 < q, "INVALID_INPUT"); require(i.t3 < q, "INVALID_INPUT"); require(i.t4 < q, "INVALID_INPUT"); return hash_t5f6p52_internal(i.t0, i.t1, i.t2, i.t3, i.t4, q); } // // hash_t7f6p52 // struct HashInputs7 { uint t0; uint t1; uint t2; uint t3; uint t4; uint t5; uint t6; } function mix(HashInputs7 memory i, uint q) internal pure { HashInputs7 memory o; o.t0 = mulmod(i.t0, 14183033936038168803360723133013092560869148726790180682363054735190196956789, q); o.t0 = addmod(o.t0, mulmod(i.t1, 9067734253445064890734144122526450279189023719890032859456830213166173619761, q), q); o.t0 = addmod(o.t0, mulmod(i.t2, 16378664841697311562845443097199265623838619398287411428110917414833007677155, q), q); o.t0 = addmod(o.t0, mulmod(i.t3, 12968540216479938138647596899147650021419273189336843725176422194136033835172, q), q); o.t0 = addmod(o.t0, mulmod(i.t4, 3636162562566338420490575570584278737093584021456168183289112789616069756675, q), q); o.t0 = addmod(o.t0, mulmod(i.t5, 8949952361235797771659501126471156178804092479420606597426318793013844305422, q), q); o.t0 = addmod(o.t0, mulmod(i.t6, 13586657904816433080148729258697725609063090799921401830545410130405357110367, q), q); o.t1 = mulmod(i.t0, 2799255644797227968811798608332314218966179365168250111693473252876996230317, q); o.t1 = addmod(o.t1, mulmod(i.t1, 2482058150180648511543788012634934806465808146786082148795902594096349483974, q), q); o.t1 = addmod(o.t1, mulmod(i.t2, 16563522740626180338295201738437974404892092704059676533096069531044355099628, q), q); o.t1 = addmod(o.t1, mulmod(i.t3, 10468644849657689537028565510142839489302836569811003546969773105463051947124, q), q); o.t1 = addmod(o.t1, mulmod(i.t4, 3328913364598498171733622353010907641674136720305714432354138807013088636408, q), q); o.t1 = addmod(o.t1, mulmod(i.t5, 8642889650254799419576843603477253661899356105675006557919250564400804756641, q), q); o.t1 = addmod(o.t1, mulmod(i.t6, 14300697791556510113764686242794463641010174685800128469053974698256194076125, q), q); o.t2 = mulmod(i.t0, 8652975463545710606098548415650457376967119951977109072274595329619335974180, q); o.t2 = addmod(o.t2, mulmod(i.t1, 970943815872417895015626519859542525373809485973005165410533315057253476903, q), q); o.t2 = addmod(o.t2, mulmod(i.t2, 19406667490568134101658669326517700199745817783746545889094238643063688871948, q), q); o.t2 = addmod(o.t2, mulmod(i.t3, 17049854690034965250221386317058877242629221002521630573756355118745574274967, q), q); o.t2 = addmod(o.t2, mulmod(i.t4, 4964394613021008685803675656098849539153699842663541444414978877928878266244, q), q); o.t2 = addmod(o.t2, mulmod(i.t5, 15474947305445649466370538888925567099067120578851553103424183520405650587995, q), q); o.t2 = addmod(o.t2, mulmod(i.t6, 1016119095639665978105768933448186152078842964810837543326777554729232767846, q), q); o.t3 = mulmod(i.t0, 9077319817220936628089890431129759976815127354480867310384708941479362824016, q); o.t3 = addmod(o.t3, mulmod(i.t1, 4770370314098695913091200576539533727214143013236894216582648993741910829490, q), q); o.t3 = addmod(o.t3, mulmod(i.t2, 4298564056297802123194408918029088169104276109138370115401819933600955259473, q), q); o.t3 = addmod(o.t3, mulmod(i.t3, 6905514380186323693285869145872115273350947784558995755916362330070690839131, q), q); o.t3 = addmod(o.t3, mulmod(i.t4, 4783343257810358393326889022942241108539824540285247795235499223017138301952, q), q); o.t3 = addmod(o.t3, mulmod(i.t5, 1420772902128122367335354247676760257656541121773854204774788519230732373317, q), q); o.t3 = addmod(o.t3, mulmod(i.t6, 14172871439045259377975734198064051992755748777535789572469924335100006948373, q), q); o.t4 = mulmod(i.t0, 8303849270045876854140023508764676765932043944545416856530551331270859502246, q); o.t4 = addmod(o.t4, mulmod(i.t1, 20218246699596954048529384569730026273241102596326201163062133863539137060414, q), q); o.t4 = addmod(o.t4, mulmod(i.t2, 1712845821388089905746651754894206522004527237615042226559791118162382909269, q), q); o.t4 = addmod(o.t4, mulmod(i.t3, 13001155522144542028910638547179410124467185319212645031214919884423841839406, q), q); o.t4 = addmod(o.t4, mulmod(i.t4, 16037892369576300958623292723740289861626299352695838577330319504984091062115, q), q); o.t4 = addmod(o.t4, mulmod(i.t5, 19189494548480259335554606182055502469831573298885662881571444557262020106898, q), q); o.t4 = addmod(o.t4, mulmod(i.t6, 19032687447778391106390582750185144485341165205399984747451318330476859342654, q), q); o.t5 = mulmod(i.t0, 13272957914179340594010910867091459756043436017766464331915862093201960540910, q); o.t5 = addmod(o.t5, mulmod(i.t1, 9416416589114508529880440146952102328470363729880726115521103179442988482948, q), q); o.t5 = addmod(o.t5, mulmod(i.t2, 8035240799672199706102747147502951589635001418759394863664434079699838251138, q), q); o.t5 = addmod(o.t5, mulmod(i.t3, 21642389080762222565487157652540372010968704000567605990102641816691459811717, q), q); o.t5 = addmod(o.t5, mulmod(i.t4, 20261355950827657195644012399234591122288573679402601053407151083849785332516, q), q); o.t5 = addmod(o.t5, mulmod(i.t5, 14514189384576734449268559374569145463190040567900950075547616936149781403109, q), q); o.t5 = addmod(o.t5, mulmod(i.t6, 19038036134886073991945204537416211699632292792787812530208911676638479944765, q), q); o.t6 = mulmod(i.t0, 15627836782263662543041758927100784213807648787083018234961118439434298020664, q); o.t6 = addmod(o.t6, mulmod(i.t1, 5655785191024506056588710805596292231240948371113351452712848652644610823632, q), q); o.t6 = addmod(o.t6, mulmod(i.t2, 8265264721707292643644260517162050867559314081394556886644673791575065394002, q), q); o.t6 = addmod(o.t6, mulmod(i.t3, 17151144681903609082202835646026478898625761142991787335302962548605510241586, q), q); o.t6 = addmod(o.t6, mulmod(i.t4, 18731644709777529787185361516475509623264209648904603914668024590231177708831, q), q); o.t6 = addmod(o.t6, mulmod(i.t5, 20697789991623248954020701081488146717484139720322034504511115160686216223641, q), q); o.t6 = addmod(o.t6, mulmod(i.t6, 6200020095464686209289974437830528853749866001482481427982839122465470640886, q), q); i.t0 = o.t0; i.t1 = o.t1; i.t2 = o.t2; i.t3 = o.t3; i.t4 = o.t4; i.t5 = o.t5; i.t6 = o.t6; } function ark(HashInputs7 memory i, uint q, uint c) internal pure { HashInputs7 memory o; o.t0 = addmod(i.t0, c, q); o.t1 = addmod(i.t1, c, q); o.t2 = addmod(i.t2, c, q); o.t3 = addmod(i.t3, c, q); o.t4 = addmod(i.t4, c, q); o.t5 = addmod(i.t5, c, q); o.t6 = addmod(i.t6, c, q); i.t0 = o.t0; i.t1 = o.t1; i.t2 = o.t2; i.t3 = o.t3; i.t4 = o.t4; i.t5 = o.t5; i.t6 = o.t6; } function sbox_full(HashInputs7 memory i, uint q) internal pure { HashInputs7 memory o; o.t0 = mulmod(i.t0, i.t0, q); o.t0 = mulmod(o.t0, o.t0, q); o.t0 = mulmod(i.t0, o.t0, q); o.t1 = mulmod(i.t1, i.t1, q); o.t1 = mulmod(o.t1, o.t1, q); o.t1 = mulmod(i.t1, o.t1, q); o.t2 = mulmod(i.t2, i.t2, q); o.t2 = mulmod(o.t2, o.t2, q); o.t2 = mulmod(i.t2, o.t2, q); o.t3 = mulmod(i.t3, i.t3, q); o.t3 = mulmod(o.t3, o.t3, q); o.t3 = mulmod(i.t3, o.t3, q); o.t4 = mulmod(i.t4, i.t4, q); o.t4 = mulmod(o.t4, o.t4, q); o.t4 = mulmod(i.t4, o.t4, q); o.t5 = mulmod(i.t5, i.t5, q); o.t5 = mulmod(o.t5, o.t5, q); o.t5 = mulmod(i.t5, o.t5, q); o.t6 = mulmod(i.t6, i.t6, q); o.t6 = mulmod(o.t6, o.t6, q); o.t6 = mulmod(i.t6, o.t6, q); i.t0 = o.t0; i.t1 = o.t1; i.t2 = o.t2; i.t3 = o.t3; i.t4 = o.t4; i.t5 = o.t5; i.t6 = o.t6; } function sbox_partial(HashInputs7 memory i, uint q) internal pure { HashInputs7 memory o; o.t0 = mulmod(i.t0, i.t0, q); o.t0 = mulmod(o.t0, o.t0, q); o.t0 = mulmod(i.t0, o.t0, q); i.t0 = o.t0; } function hash_t7f6p52(HashInputs7 memory i, uint q) internal pure returns (uint) { // validate inputs require(i.t0 < q, "INVALID_INPUT"); require(i.t1 < q, "INVALID_INPUT"); require(i.t2 < q, "INVALID_INPUT"); require(i.t3 < q, "INVALID_INPUT"); require(i.t4 < q, "INVALID_INPUT"); require(i.t5 < q, "INVALID_INPUT"); require(i.t6 < q, "INVALID_INPUT"); // round 0 ark(i, q, 14397397413755236225575615486459253198602422701513067526754101844196324375522); sbox_full(i, q); mix(i, q); // round 1 ark(i, q, 10405129301473404666785234951972711717481302463898292859783056520670200613128); sbox_full(i, q); mix(i, q); // round 2 ark(i, q, 5179144822360023508491245509308555580251733042407187134628755730783052214509); sbox_full(i, q); mix(i, q); // round 3 ark(i, q, 9132640374240188374542843306219594180154739721841249568925550236430986592615); sbox_partial(i, q); mix(i, q); // round 4 ark(i, q, 20360807315276763881209958738450444293273549928693737723235350358403012458514); sbox_partial(i, q); mix(i, q); // round 5 ark(i, q, 17933600965499023212689924809448543050840131883187652471064418452962948061619); sbox_partial(i, q); mix(i, q); // round 6 ark(i, q, 3636213416533737411392076250708419981662897009810345015164671602334517041153); sbox_partial(i, q); mix(i, q); // round 7 ark(i, q, 2008540005368330234524962342006691994500273283000229509835662097352946198608); sbox_partial(i, q); mix(i, q); // round 8 ark(i, q, 16018407964853379535338740313053768402596521780991140819786560130595652651567); sbox_partial(i, q); mix(i, q); // round 9 ark(i, q, 20653139667070586705378398435856186172195806027708437373983929336015162186471); sbox_partial(i, q); mix(i, q); // round 10 ark(i, q, 17887713874711369695406927657694993484804203950786446055999405564652412116765); sbox_partial(i, q); mix(i, q); // round 11 ark(i, q, 4852706232225925756777361208698488277369799648067343227630786518486608711772); sbox_partial(i, q); mix(i, q); // round 12 ark(i, q, 8969172011633935669771678412400911310465619639756845342775631896478908389850); sbox_partial(i, q); mix(i, q); // round 13 ark(i, q, 20570199545627577691240476121888846460936245025392381957866134167601058684375); sbox_partial(i, q); mix(i, q); // round 14 ark(i, q, 16442329894745639881165035015179028112772410105963688121820543219662832524136); sbox_partial(i, q); mix(i, q); // round 15 ark(i, q, 20060625627350485876280451423010593928172611031611836167979515653463693899374); sbox_partial(i, q); mix(i, q); // round 16 ark(i, q, 16637282689940520290130302519163090147511023430395200895953984829546679599107); sbox_partial(i, q); mix(i, q); // round 17 ark(i, q, 15599196921909732993082127725908821049411366914683565306060493533569088698214); sbox_partial(i, q); mix(i, q); // round 18 ark(i, q, 16894591341213863947423904025624185991098788054337051624251730868231322135455); sbox_partial(i, q); mix(i, q); // round 19 ark(i, q, 1197934381747032348421303489683932612752526046745577259575778515005162320212); sbox_partial(i, q); mix(i, q); // round 20 ark(i, q, 6172482022646932735745595886795230725225293469762393889050804649558459236626); sbox_partial(i, q); mix(i, q); // round 21 ark(i, q, 21004037394166516054140386756510609698837211370585899203851827276330669555417); sbox_partial(i, q); mix(i, q); // round 22 ark(i, q, 15262034989144652068456967541137853724140836132717012646544737680069032573006); sbox_partial(i, q); mix(i, q); // round 23 ark(i, q, 15017690682054366744270630371095785995296470601172793770224691982518041139766); sbox_partial(i, q); mix(i, q); // round 24 ark(i, q, 15159744167842240513848638419303545693472533086570469712794583342699782519832); sbox_partial(i, q); mix(i, q); // round 25 ark(i, q, 11178069035565459212220861899558526502477231302924961773582350246646450941231); sbox_partial(i, q); mix(i, q); // round 26 ark(i, q, 21154888769130549957415912997229564077486639529994598560737238811887296922114); sbox_partial(i, q); mix(i, q); // round 27 ark(i, q, 20162517328110570500010831422938033120419484532231241180224283481905744633719); sbox_partial(i, q); mix(i, q); // round 28 ark(i, q, 2777362604871784250419758188173029886707024739806641263170345377816177052018); sbox_partial(i, q); mix(i, q); // round 29 ark(i, q, 15732290486829619144634131656503993123618032247178179298922551820261215487562); sbox_partial(i, q); mix(i, q); // round 30 ark(i, q, 6024433414579583476444635447152826813568595303270846875177844482142230009826); sbox_partial(i, q); mix(i, q); // round 31 ark(i, q, 17677827682004946431939402157761289497221048154630238117709539216286149983245); sbox_partial(i, q); mix(i, q); // round 32 ark(i, q, 10716307389353583413755237303156291454109852751296156900963208377067748518748); sbox_partial(i, q); mix(i, q); // round 33 ark(i, q, 14925386988604173087143546225719076187055229908444910452781922028996524347508); sbox_partial(i, q); mix(i, q); // round 34 ark(i, q, 8940878636401797005293482068100797531020505636124892198091491586778667442523); sbox_partial(i, q); mix(i, q); // round 35 ark(i, q, 18911747154199663060505302806894425160044925686870165583944475880789706164410); sbox_partial(i, q); mix(i, q); // round 36 ark(i, q, 8821532432394939099312235292271438180996556457308429936910969094255825456935); sbox_partial(i, q); mix(i, q); // round 37 ark(i, q, 20632576502437623790366878538516326728436616723089049415538037018093616927643); sbox_partial(i, q); mix(i, q); // round 38 ark(i, q, 71447649211767888770311304010816315780740050029903404046389165015534756512); sbox_partial(i, q); mix(i, q); // round 39 ark(i, q, 2781996465394730190470582631099299305677291329609718650018200531245670229393); sbox_partial(i, q); mix(i, q); // round 40 ark(i, q, 12441376330954323535872906380510501637773629931719508864016287320488688345525); sbox_partial(i, q); mix(i, q); // round 41 ark(i, q, 2558302139544901035700544058046419714227464650146159803703499681139469546006); sbox_partial(i, q); mix(i, q); // round 42 ark(i, q, 10087036781939179132584550273563255199577525914374285705149349445480649057058); sbox_partial(i, q); mix(i, q); // round 43 ark(i, q, 4267692623754666261749551533667592242661271409704769363166965280715887854739); sbox_partial(i, q); mix(i, q); // round 44 ark(i, q, 4945579503584457514844595640661884835097077318604083061152997449742124905548); sbox_partial(i, q); mix(i, q); // round 45 ark(i, q, 17742335354489274412669987990603079185096280484072783973732137326144230832311); sbox_partial(i, q); mix(i, q); // round 46 ark(i, q, 6266270088302506215402996795500854910256503071464802875821837403486057988208); sbox_partial(i, q); mix(i, q); // round 47 ark(i, q, 2716062168542520412498610856550519519760063668165561277991771577403400784706); sbox_partial(i, q); mix(i, q); // round 48 ark(i, q, 19118392018538203167410421493487769944462015419023083813301166096764262134232); sbox_partial(i, q); mix(i, q); // round 49 ark(i, q, 9386595745626044000666050847309903206827901310677406022353307960932745699524); sbox_partial(i, q); mix(i, q); // round 50 ark(i, q, 9121640807890366356465620448383131419933298563527245687958865317869840082266); sbox_partial(i, q); mix(i, q); // round 51 ark(i, q, 3078975275808111706229899605611544294904276390490742680006005661017864583210); sbox_partial(i, q); mix(i, q); // round 52 ark(i, q, 7157404299437167354719786626667769956233708887934477609633504801472827442743); sbox_partial(i, q); mix(i, q); // round 53 ark(i, q, 14056248655941725362944552761799461694550787028230120190862133165195793034373); sbox_partial(i, q); mix(i, q); // round 54 ark(i, q, 14124396743304355958915937804966111851843703158171757752158388556919187839849); sbox_partial(i, q); mix(i, q); // round 55 ark(i, q, 11851254356749068692552943732920045260402277343008629727465773766468466181076); sbox_full(i, q); mix(i, q); // round 56 ark(i, q, 9799099446406796696742256539758943483211846559715874347178722060519817626047); sbox_full(i, q); mix(i, q); // round 57 ark(i, q, 10156146186214948683880719664738535455146137901666656566575307300522957959544); sbox_full(i, q); mix(i, q); return i.t0; } } // File: contracts/core/impl/libexchange/ExchangeTokens.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeTokens. /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library ExchangeTokens { using MathUint for uint; using ERC20SafeTransfer for address; using ExchangeMode for ExchangeData.State; event TokenRegistered( address token, uint16 tokenId ); function getTokenAddress( ExchangeData.State storage S, uint16 tokenID ) public view returns (address) { require(tokenID < S.tokens.length, "INVALID_TOKEN_ID"); return S.tokens[tokenID].token; } function registerToken( ExchangeData.State storage S, address tokenAddress ) public returns (uint16 tokenID) { require(!S.isInWithdrawalMode(), "INVALID_MODE"); require(S.tokenToTokenId[tokenAddress] == 0, "TOKEN_ALREADY_EXIST"); require(S.tokens.length < ExchangeData.NFT_TOKEN_ID_START, "TOKEN_REGISTRY_FULL"); // Check if the deposit contract supports the new token if (S.depositContract != IDepositContract(0)) { require( S.depositContract.isTokenSupported(tokenAddress), "UNSUPPORTED_TOKEN" ); } // Assign a tokenID and store the token ExchangeData.Token memory token = ExchangeData.Token( tokenAddress ); tokenID = uint16(S.tokens.length); S.tokens.push(token); S.tokenToTokenId[tokenAddress] = tokenID + 1; emit TokenRegistered(tokenAddress, tokenID); } function getTokenID( ExchangeData.State storage S, address tokenAddress ) internal // inline call view returns (uint16 tokenID) { tokenID = S.tokenToTokenId[tokenAddress]; require(tokenID != 0, "TOKEN_NOT_FOUND"); tokenID = tokenID - 1; } function isNFT(uint16 tokenID) internal // inline call pure returns (bool) { return tokenID >= ExchangeData.NFT_TOKEN_ID_START; } } // File: contracts/core/impl/libexchange/ExchangeBalances.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeBalances. /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library ExchangeBalances { using ExchangeTokens for uint16; using MathUint for uint; function verifyAccountBalance( uint merkleRoot, ExchangeData.MerkleProof calldata merkleProof ) public pure { require( isAccountBalanceCorrect(merkleRoot, merkleProof), "INVALID_MERKLE_TREE_DATA" ); } function isAccountBalanceCorrect( uint merkleRoot, ExchangeData.MerkleProof memory merkleProof ) public pure returns (bool) { // Calculate the Merkle root using the Merkle paths provided uint calculatedRoot = getBalancesRoot( merkleProof.balanceLeaf.tokenID, merkleProof.balanceLeaf.balance, merkleProof.balanceLeaf.weightAMM, merkleProof.balanceLeaf.storageRoot, merkleProof.balanceMerkleProof ); calculatedRoot = getAccountInternalsRoot( merkleProof.accountLeaf.accountID, merkleProof.accountLeaf.owner, merkleProof.accountLeaf.pubKeyX, merkleProof.accountLeaf.pubKeyY, merkleProof.accountLeaf.nonce, merkleProof.accountLeaf.feeBipsAMM, calculatedRoot, merkleProof.accountMerkleProof ); if (merkleProof.balanceLeaf.tokenID.isNFT()) { // Verify the NFT data uint minter = uint(merkleProof.nft.minter); uint nftType = uint(merkleProof.nft.nftType); uint token = uint(merkleProof.nft.token); uint nftIDLo = merkleProof.nft.nftID & 0xffffffffffffffffffffffffffffffff; uint nftIDHi = merkleProof.nft.nftID >> 128; uint creatorFeeBips = merkleProof.nft.creatorFeeBips; Poseidon.HashInputs7 memory inputs = Poseidon.HashInputs7( minter, nftType, token, nftIDLo, nftIDHi, creatorFeeBips, 0 ); uint nftData = Poseidon.hash_t7f6p52(inputs, ExchangeData.SNARK_SCALAR_FIELD); if (nftData != merkleProof.balanceLeaf.weightAMM) { return false; } } // Check against the expected Merkle root return (calculatedRoot == merkleRoot); } function getBalancesRoot( uint16 tokenID, uint balance, uint weightAMM, uint storageRoot, uint[24] memory balanceMerkleProof ) private pure returns (uint) { // Hash the balance leaf uint balanceItem = hashImpl(balance, weightAMM, storageRoot, 0); // Calculate the Merkle root of the balance quad Merkle tree uint _id = tokenID; for (uint depth = 0; depth < 8; depth++) { uint base = depth * 3; if (_id & 3 == 0) { balanceItem = hashImpl( balanceItem, balanceMerkleProof[base], balanceMerkleProof[base + 1], balanceMerkleProof[base + 2] ); } else if (_id & 3 == 1) { balanceItem = hashImpl( balanceMerkleProof[base], balanceItem, balanceMerkleProof[base + 1], balanceMerkleProof[base + 2] ); } else if (_id & 3 == 2) { balanceItem = hashImpl( balanceMerkleProof[base], balanceMerkleProof[base + 1], balanceItem, balanceMerkleProof[base + 2] ); } else if (_id & 3 == 3) { balanceItem = hashImpl( balanceMerkleProof[base], balanceMerkleProof[base + 1], balanceMerkleProof[base + 2], balanceItem ); } _id = _id >> 2; } return balanceItem; } function getAccountInternalsRoot( uint32 accountID, address owner, uint pubKeyX, uint pubKeyY, uint nonce, uint feeBipsAMM, uint balancesRoot, uint[48] memory accountMerkleProof ) private pure returns (uint) { // Hash the account leaf uint accountItem = hashAccountLeaf(uint(owner), pubKeyX, pubKeyY, nonce, feeBipsAMM, balancesRoot); // Calculate the Merkle root of the account quad Merkle tree uint _id = accountID; for (uint depth = 0; depth < 16; depth++) { uint base = depth * 3; if (_id & 3 == 0) { accountItem = hashImpl( accountItem, accountMerkleProof[base], accountMerkleProof[base + 1], accountMerkleProof[base + 2] ); } else if (_id & 3 == 1) { accountItem = hashImpl( accountMerkleProof[base], accountItem, accountMerkleProof[base + 1], accountMerkleProof[base + 2] ); } else if (_id & 3 == 2) { accountItem = hashImpl( accountMerkleProof[base], accountMerkleProof[base + 1], accountItem, accountMerkleProof[base + 2] ); } else if (_id & 3 == 3) { accountItem = hashImpl( accountMerkleProof[base], accountMerkleProof[base + 1], accountMerkleProof[base + 2], accountItem ); } _id = _id >> 2; } return accountItem; } function hashAccountLeaf( uint t0, uint t1, uint t2, uint t3, uint t4, uint t5 ) public pure returns (uint) { Poseidon.HashInputs7 memory inputs = Poseidon.HashInputs7(t0, t1, t2, t3, t4, t5, 0); return Poseidon.hash_t7f6p52(inputs, ExchangeData.SNARK_SCALAR_FIELD); } function hashImpl( uint t0, uint t1, uint t2, uint t3 ) private pure returns (uint) { Poseidon.HashInputs5 memory inputs = Poseidon.HashInputs5(t0, t1, t2, t3, 0); return Poseidon.hash_t5f6p52(inputs, ExchangeData.SNARK_SCALAR_FIELD); } } // File: contracts/thirdparty/BytesUtil.sol //Mainly taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol library BytesUtil { function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore(0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. )) } return tempBytes; } function slice( bytes memory _bytes, uint _start, uint _length ) internal pure returns (bytes memory) { require(_bytes.length >= (_start + _length)); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint _start) internal pure returns (address) { require(_bytes.length >= (_start + 20)); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint _start) internal pure returns (uint8) { require(_bytes.length >= (_start + 1)); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint _start) internal pure returns (uint16) { require(_bytes.length >= (_start + 2)); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint24(bytes memory _bytes, uint _start) internal pure returns (uint24) { require(_bytes.length >= (_start + 3)); uint24 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x3), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint _start) internal pure returns (uint32) { require(_bytes.length >= (_start + 4)); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint _start) internal pure returns (uint64) { require(_bytes.length >= (_start + 8)); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint _start) internal pure returns (uint96) { require(_bytes.length >= (_start + 12)); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint _start) internal pure returns (uint128) { require(_bytes.length >= (_start + 16)); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint(bytes memory _bytes, uint _start) internal pure returns (uint256) { require(_bytes.length >= (_start + 32)); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes4(bytes memory _bytes, uint _start) internal pure returns (bytes4) { require(_bytes.length >= (_start + 4)); bytes4 tempBytes4; assembly { tempBytes4 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes4; } function toBytes20(bytes memory _bytes, uint _start) internal pure returns (bytes20) { require(_bytes.length >= (_start + 20)); bytes20 tempBytes20; assembly { tempBytes20 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes20; } function toBytes32(bytes memory _bytes, uint _start) internal pure returns (bytes32) { require(_bytes.length >= (_start + 32)); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function toAddressUnsafe(bytes memory _bytes, uint _start) internal pure returns (address) { address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8Unsafe(bytes memory _bytes, uint _start) internal pure returns (uint8) { uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16Unsafe(bytes memory _bytes, uint _start) internal pure returns (uint16) { uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint24Unsafe(bytes memory _bytes, uint _start) internal pure returns (uint24) { uint24 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x3), _start)) } return tempUint; } function toUint32Unsafe(bytes memory _bytes, uint _start) internal pure returns (uint32) { uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64Unsafe(bytes memory _bytes, uint _start) internal pure returns (uint64) { uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96Unsafe(bytes memory _bytes, uint _start) internal pure returns (uint96) { uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128Unsafe(bytes memory _bytes, uint _start) internal pure returns (uint128) { uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUintUnsafe(bytes memory _bytes, uint _start) internal pure returns (uint256) { uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes4Unsafe(bytes memory _bytes, uint _start) internal pure returns (bytes4) { bytes4 tempBytes4; assembly { tempBytes4 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes4; } function toBytes20Unsafe(bytes memory _bytes, uint _start) internal pure returns (bytes20) { bytes20 tempBytes20; assembly { tempBytes20 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes20; } function toBytes32Unsafe(bytes memory _bytes, uint _start) internal pure returns (bytes32) { bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function fastSHA256( bytes memory data ) internal view returns (bytes32) { bytes32[] memory result = new bytes32[](1); bool success; assembly { let ptr := add(data, 32) success := staticcall(sub(gas(), 2000), 2, ptr, mload(data), add(result, 32), 32) } require(success, "SHA256_FAILED"); return result[0]; } } // File: contracts/core/impl/libtransactions/BlockReader.sol // Copyright 2017 Loopring Technology Limited. /// @title BlockReader /// @author Brecht Devos - <[email protected]> /// @dev Utility library to read block data. library BlockReader { using BlockReader for ExchangeData.Block; using BytesUtil for bytes; uint public constant OFFSET_TO_TRANSACTIONS = 20 + 32 + 32 + 4 + 1 + 1 + 4 + 4; struct BlockHeader { address exchange; bytes32 merkleRootBefore; bytes32 merkleRootAfter; uint32 timestamp; uint8 protocolTakerFeeBips; uint8 protocolMakerFeeBips; uint32 numConditionalTransactions; uint32 operatorAccountID; } function readHeader( bytes memory _blockData ) internal pure returns (BlockHeader memory header) { uint offset = 0; header.exchange = _blockData.toAddress(offset); offset += 20; header.merkleRootBefore = _blockData.toBytes32(offset); offset += 32; header.merkleRootAfter = _blockData.toBytes32(offset); offset += 32; header.timestamp = _blockData.toUint32(offset); offset += 4; header.protocolTakerFeeBips = _blockData.toUint8(offset); offset += 1; header.protocolMakerFeeBips = _blockData.toUint8(offset); offset += 1; header.numConditionalTransactions = _blockData.toUint32(offset); offset += 4; header.operatorAccountID = _blockData.toUint32(offset); offset += 4; assert(offset == OFFSET_TO_TRANSACTIONS); } function readTransactionData( bytes memory data, uint txIdx, uint blockSize, bytes memory txData ) internal pure { require(txIdx < blockSize, "INVALID_TX_IDX"); // The transaction was transformed to make it easier to compress. // Transform it back here. // Part 1 uint txDataOffset = OFFSET_TO_TRANSACTIONS + txIdx * ExchangeData.TX_DATA_AVAILABILITY_SIZE_PART_1; assembly { mstore(add(txData, 32), mload(add(data, add(txDataOffset, 32)))) } // Part 2 txDataOffset = OFFSET_TO_TRANSACTIONS + blockSize * ExchangeData.TX_DATA_AVAILABILITY_SIZE_PART_1 + txIdx * ExchangeData.TX_DATA_AVAILABILITY_SIZE_PART_2; assembly { mstore(add(txData, 61 /*32 + 29*/), mload(add(data, add(txDataOffset, 32)))) mstore(add(txData, 68 ), mload(add(data, add(txDataOffset, 39)))) } } } // File: contracts/thirdparty/SafeCast.sol // Taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/SafeCast.sol /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { require(value < 2**96, "SafeCast: value doesn\'t fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 40 bits */ function toUint40(uint256 value) internal pure returns (uint40) { require(value < 2**40, "SafeCast: value doesn\'t fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128) { require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits"); return int128(value); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64) { require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits"); return int64(value); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32) { require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits"); return int32(value); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16) { require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits"); return int16(value); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits. * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8) { require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits"); return int8(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { require(value < 2**255, "SafeCast: value doesn't fit in an int256"); return int256(value); } } // File: contracts/lib/FloatUtil.sol // Copyright 2017 Loopring Technology Limited. /// @title Utility Functions for floats /// @author Brecht Devos - <[email protected]> library FloatUtil { using MathUint for uint; using SafeCast for uint; // Decodes a decimal float value that is encoded like `exponent | mantissa`. // Both exponent and mantissa are in base 10. // Decoding to an integer is as simple as `mantissa * (10 ** exponent)` // Will throw when the decoded value overflows an uint96 /// @param f The float value with 5 bits for the exponent /// @param numBits The total number of bits (numBitsMantissa := numBits - numBitsExponent) /// @return value The decoded integer value. function decodeFloat( uint f, uint numBits ) internal pure returns (uint96 value) { if (f == 0) { return 0; } uint numBitsMantissa = numBits.sub(5); uint exponent = f >> numBitsMantissa; // log2(10**77) = 255.79 < 256 require(exponent <= 77, "EXPONENT_TOO_LARGE"); uint mantissa = f & ((1 << numBitsMantissa) - 1); value = mantissa.mul(10 ** exponent).toUint96(); } // Decodes a decimal float value that is encoded like `exponent | mantissa`. // Both exponent and mantissa are in base 10. // Decoding to an integer is as simple as `mantissa * (10 ** exponent)` // Will throw when the decoded value overflows an uint96 /// @param f The float value with 5 bits exponent, 11 bits mantissa /// @return value The decoded integer value. function decodeFloat16( uint16 f ) internal pure returns (uint96) { uint value = ((uint(f) & 2047) * (10 ** (uint(f) >> 11))); require(value < 2**96, "SafeCast: value doesn\'t fit in 96 bits"); return uint96(value); } // Decodes a decimal float value that is encoded like `exponent | mantissa`. // Both exponent and mantissa are in base 10. // Decoding to an integer is as simple as `mantissa * (10 ** exponent)` // Will throw when the decoded value overflows an uint96 /// @param f The float value with 5 bits exponent, 19 bits mantissa /// @return value The decoded integer value. function decodeFloat24( uint24 f ) internal pure returns (uint96) { uint value = ((uint(f) & 524287) * (10 ** (uint(f) >> 19))); require(value < 2**96, "SafeCast: value doesn\'t fit in 96 bits"); return uint96(value); } } // File: contracts/lib/ERC1271.sol // Copyright 2017 Loopring Technology Limited. abstract contract ERC1271 { // bytes4(keccak256("isValidSignature(bytes32,bytes)") bytes4 constant internal ERC1271_MAGICVALUE = 0x1626ba7e; function isValidSignature( bytes32 _hash, bytes memory _signature) public view virtual returns (bytes4 magicValueB32); } // File: contracts/lib/SignatureUtil.sol // Copyright 2017 Loopring Technology Limited. /// @title SignatureUtil /// @author Daniel Wang - <[email protected]> /// @dev This method supports multihash standard. Each signature's last byte indicates /// the signature's type. library SignatureUtil { using BytesUtil for bytes; using MathUint for uint; using AddressUtil for address; enum SignatureType { ILLEGAL, INVALID, EIP_712, ETH_SIGN, WALLET // deprecated } bytes4 constant internal ERC1271_MAGICVALUE = 0x1626ba7e; function verifySignatures( bytes32 signHash, address[] memory signers, bytes[] memory signatures ) internal view returns (bool) { require(signers.length == signatures.length, "BAD_SIGNATURE_DATA"); address lastSigner; for (uint i = 0; i < signers.length; i++) { require(signers[i] > lastSigner, "INVALID_SIGNERS_ORDER"); lastSigner = signers[i]; if (!verifySignature(signHash, signers[i], signatures[i])) { return false; } } return true; } function verifySignature( bytes32 signHash, address signer, bytes memory signature ) internal view returns (bool) { if (signer == address(0)) { return false; } return signer.isContract()? verifyERC1271Signature(signHash, signer, signature): verifyEOASignature(signHash, signer, signature); } function recoverECDSASigner( bytes32 signHash, bytes memory signature ) internal pure returns (address) { if (signature.length != 65) { return address(0); } bytes32 r; bytes32 s; uint8 v; // we jump 32 (0x20) as the first slot of bytes contains the length // we jump 65 (0x41) per signature // for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := and(mload(add(signature, 0x41)), 0xff) } // See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return address(0); } if (v == 27 || v == 28) { return ecrecover(signHash, v, r, s); } else { return address(0); } } function verifyEOASignature( bytes32 signHash, address signer, bytes memory signature ) private pure returns (bool success) { if (signer == address(0)) { return false; } uint signatureTypeOffset = signature.length.sub(1); SignatureType signatureType = SignatureType(signature.toUint8(signatureTypeOffset)); // Strip off the last byte of the signature by updating the length assembly { mstore(signature, signatureTypeOffset) } if (signatureType == SignatureType.EIP_712) { success = (signer == recoverECDSASigner(signHash, signature)); } else if (signatureType == SignatureType.ETH_SIGN) { bytes32 hash = keccak256( abi.encodePacked("\x19Ethereum Signed Message:\n32", signHash) ); success = (signer == recoverECDSASigner(hash, signature)); } else { success = false; } // Restore the signature length assembly { mstore(signature, add(signatureTypeOffset, 1)) } return success; } function verifyERC1271Signature( bytes32 signHash, address signer, bytes memory signature ) private view returns (bool) { bytes memory callData = abi.encodeWithSelector( ERC1271.isValidSignature.selector, signHash, signature ); (bool success, bytes memory result) = signer.staticcall(callData); return ( success && result.length == 32 && result.toBytes4(0) == ERC1271_MAGICVALUE ); } } // File: contracts/core/impl/libexchange/ExchangeSignatures.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeSignatures. /// @dev All methods in this lib are internal, therefore, there is no need /// to deploy this library independently. /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> library ExchangeSignatures { using SignatureUtil for bytes32; function requireAuthorizedTx( ExchangeData.State storage S, address signer, bytes memory signature, bytes32 txHash ) internal // inline call { require(signer != address(0), "INVALID_SIGNER"); // Verify the signature if one is provided, otherwise fall back to an approved tx if (signature.length > 0) { require(txHash.verifySignature(signer, signature), "INVALID_SIGNATURE"); } else { require(S.approvedTx[signer][txHash], "TX_NOT_APPROVED"); delete S.approvedTx[signer][txHash]; } } } // File: contracts/core/impl/libtransactions/AccountUpdateTransaction.sol // Copyright 2017 Loopring Technology Limited. /// @title AccountUpdateTransaction /// @author Brecht Devos - <[email protected]> library AccountUpdateTransaction { using BytesUtil for bytes; using FloatUtil for uint16; using ExchangeSignatures for ExchangeData.State; bytes32 constant public ACCOUNTUPDATE_TYPEHASH = keccak256( "AccountUpdate(address owner,uint32 accountID,uint16 feeTokenID,uint96 maxFee,uint256 publicKey,uint32 validUntil,uint32 nonce)" ); struct AccountUpdate { address owner; uint32 accountID; uint16 feeTokenID; uint96 maxFee; uint96 fee; uint publicKey; uint32 validUntil; uint32 nonce; } // Auxiliary data for each account update struct AccountUpdateAuxiliaryData { bytes signature; uint96 maxFee; uint32 validUntil; } function process( ExchangeData.State storage S, ExchangeData.BlockContext memory ctx, bytes memory data, uint offset, bytes memory auxiliaryData ) internal { // Read the account update AccountUpdate memory accountUpdate; readTx(data, offset, accountUpdate); AccountUpdateAuxiliaryData memory auxData = abi.decode(auxiliaryData, (AccountUpdateAuxiliaryData)); // Fill in withdrawal data missing from DA accountUpdate.validUntil = auxData.validUntil; accountUpdate.maxFee = auxData.maxFee == 0 ? accountUpdate.fee : auxData.maxFee; // Validate require(ctx.timestamp < accountUpdate.validUntil, "ACCOUNT_UPDATE_EXPIRED"); require(accountUpdate.fee <= accountUpdate.maxFee, "ACCOUNT_UPDATE_FEE_TOO_HIGH"); // Calculate the tx hash bytes32 txHash = hashTx(ctx.DOMAIN_SEPARATOR, accountUpdate); // Check onchain authorization S.requireAuthorizedTx(accountUpdate.owner, auxData.signature, txHash); } function readTx( bytes memory data, uint offset, AccountUpdate memory accountUpdate ) internal pure { uint _offset = offset; require(data.toUint8Unsafe(_offset) == uint8(ExchangeData.TransactionType.ACCOUNT_UPDATE), "INVALID_TX_TYPE"); _offset += 1; // Check that this is a conditional offset require(data.toUint8Unsafe(_offset) == 1, "INVALID_AUXILIARYDATA_DATA"); _offset += 1; // Extract the data from the tx data // We don't use abi.decode for this because of the large amount of zero-padding // bytes the circuit would also have to hash. accountUpdate.owner = data.toAddressUnsafe(_offset); _offset += 20; accountUpdate.accountID = data.toUint32Unsafe(_offset); _offset += 4; accountUpdate.feeTokenID = data.toUint16Unsafe(_offset); _offset += 2; accountUpdate.fee = data.toUint16Unsafe(_offset).decodeFloat16(); _offset += 2; accountUpdate.publicKey = data.toUintUnsafe(_offset); _offset += 32; accountUpdate.nonce = data.toUint32Unsafe(_offset); _offset += 4; } function hashTx( bytes32 DOMAIN_SEPARATOR, AccountUpdate memory accountUpdate ) internal pure returns (bytes32) { return EIP712.hashPacked( DOMAIN_SEPARATOR, keccak256( abi.encode( ACCOUNTUPDATE_TYPEHASH, accountUpdate.owner, accountUpdate.accountID, accountUpdate.feeTokenID, accountUpdate.maxFee, accountUpdate.publicKey, accountUpdate.validUntil, accountUpdate.nonce ) ) ); } } // File: contracts/core/impl/libtransactions/AmmUpdateTransaction.sol // Copyright 2017 Loopring Technology Limited. /// @title AmmUpdateTransaction /// @author Brecht Devos - <[email protected]> library AmmUpdateTransaction { using BytesUtil for bytes; using MathUint for uint; using ExchangeSignatures for ExchangeData.State; bytes32 constant public AMMUPDATE_TYPEHASH = keccak256( "AmmUpdate(address owner,uint32 accountID,uint16 tokenID,uint8 feeBips,uint96 tokenWeight,uint32 validUntil,uint32 nonce)" ); struct AmmUpdate { address owner; uint32 accountID; uint16 tokenID; uint8 feeBips; uint96 tokenWeight; uint32 validUntil; uint32 nonce; uint96 balance; } // Auxiliary data for each AMM update struct AmmUpdateAuxiliaryData { bytes signature; uint32 validUntil; } function process( ExchangeData.State storage S, ExchangeData.BlockContext memory ctx, bytes memory data, uint offset, bytes memory auxiliaryData ) internal { // Read in the AMM update AmmUpdate memory update; readTx(data, offset, update); AmmUpdateAuxiliaryData memory auxData = abi.decode(auxiliaryData, (AmmUpdateAuxiliaryData)); // Check validUntil require(ctx.timestamp < auxData.validUntil, "AMM_UPDATE_EXPIRED"); update.validUntil = auxData.validUntil; // Calculate the tx hash bytes32 txHash = hashTx(ctx.DOMAIN_SEPARATOR, update); // Check the on-chain authorization S.requireAuthorizedTx(update.owner, auxData.signature, txHash); } function readTx( bytes memory data, uint offset, AmmUpdate memory update ) internal pure { uint _offset = offset; require(data.toUint8Unsafe(_offset) == uint8(ExchangeData.TransactionType.AMM_UPDATE), "INVALID_TX_TYPE"); _offset += 1; // We don't use abi.decode for this because of the large amount of zero-padding // bytes the circuit would also have to hash. update.owner = data.toAddressUnsafe(_offset); _offset += 20; update.accountID = data.toUint32Unsafe(_offset); _offset += 4; update.tokenID = data.toUint16Unsafe(_offset); _offset += 2; update.feeBips = data.toUint8Unsafe(_offset); _offset += 1; update.tokenWeight = data.toUint96Unsafe(_offset); _offset += 12; update.nonce = data.toUint32Unsafe(_offset); _offset += 4; update.balance = data.toUint96Unsafe(_offset); _offset += 12; } function hashTx( bytes32 DOMAIN_SEPARATOR, AmmUpdate memory update ) internal pure returns (bytes32) { return EIP712.hashPacked( DOMAIN_SEPARATOR, keccak256( abi.encode( AMMUPDATE_TYPEHASH, update.owner, update.accountID, update.tokenID, update.feeBips, update.tokenWeight, update.validUntil, update.nonce ) ) ); } } // File: contracts/lib/MathUint96.sol // Copyright 2017 Loopring Technology Limited. /// @title Utility Functions for uint /// @author Daniel Wang - <[email protected]> library MathUint96 { function add( uint96 a, uint96 b ) internal pure returns (uint96 c) { c = a + b; require(c >= a, "ADD_OVERFLOW"); } function sub( uint96 a, uint96 b ) internal pure returns (uint96 c) { require(b <= a, "SUB_UNDERFLOW"); return a - b; } } // File: contracts/core/impl/libtransactions/DepositTransaction.sol // Copyright 2017 Loopring Technology Limited. /// @title DepositTransaction /// @author Brecht Devos - <[email protected]> library DepositTransaction { using BytesUtil for bytes; using MathUint96 for uint96; struct Deposit { address to; uint32 toAccountID; uint16 tokenID; uint96 amount; } function process( ExchangeData.State storage S, ExchangeData.BlockContext memory /*ctx*/, bytes memory data, uint offset, bytes memory /*auxiliaryData*/ ) internal { // Read in the deposit Deposit memory deposit; readTx(data, offset, deposit); if (deposit.amount == 0) { return; } // Process the deposit ExchangeData.Deposit memory pendingDeposit = S.pendingDeposits[deposit.to][deposit.tokenID]; // Make sure the deposit was actually done require(pendingDeposit.timestamp > 0, "DEPOSIT_NOT_EXIST"); // Processing partial amounts of the deposited amount is allowed. // This is done to ensure the user can do multiple deposits after each other // without invalidating work done by the exchange owner for previous deposit amounts. require(pendingDeposit.amount >= deposit.amount, "INVALID_AMOUNT"); pendingDeposit.amount = pendingDeposit.amount.sub(deposit.amount); // If the deposit was fully consumed, reset it so the storage is freed up // and the owner receives a gas refund. if (pendingDeposit.amount == 0) { delete S.pendingDeposits[deposit.to][deposit.tokenID]; } else { S.pendingDeposits[deposit.to][deposit.tokenID] = pendingDeposit; } } function readTx( bytes memory data, uint offset, Deposit memory deposit ) internal pure { uint _offset = offset; require(data.toUint8Unsafe(_offset) == uint8(ExchangeData.TransactionType.DEPOSIT), "INVALID_TX_TYPE"); _offset += 1; // We don't use abi.decode for this because of the large amount of zero-padding // bytes the circuit would also have to hash. deposit.to = data.toAddressUnsafe(_offset); _offset += 20; deposit.toAccountID = data.toUint32Unsafe(_offset); _offset += 4; deposit.tokenID = data.toUint16Unsafe(_offset); _offset += 2; deposit.amount = data.toUint96Unsafe(_offset); _offset += 12; } } // File: contracts/core/impl/libtransactions/TransferTransaction.sol // Copyright 2017 Loopring Technology Limited. /// @title TransferTransaction /// @author Brecht Devos - <[email protected]> library TransferTransaction { using BytesUtil for bytes; using FloatUtil for uint24; using FloatUtil for uint16; using MathUint for uint; using ExchangeSignatures for ExchangeData.State; bytes32 constant public TRANSFER_TYPEHASH = keccak256( "Transfer(address from,address to,uint16 tokenID,uint96 amount,uint16 feeTokenID,uint96 maxFee,uint32 validUntil,uint32 storageID)" ); struct Transfer { uint32 fromAccountID; uint32 toAccountID; address from; address to; uint16 tokenID; uint96 amount; uint16 feeTokenID; uint96 maxFee; uint96 fee; uint32 validUntil; uint32 storageID; } // Auxiliary data for each transfer struct TransferAuxiliaryData { bytes signature; uint96 maxFee; uint32 validUntil; } function process( ExchangeData.State storage S, ExchangeData.BlockContext memory ctx, bytes memory data, uint offset, bytes memory auxiliaryData ) internal { // Read the transfer Transfer memory transfer; readTx(data, offset, transfer); TransferAuxiliaryData memory auxData = abi.decode(auxiliaryData, (TransferAuxiliaryData)); // Fill in withdrawal data missing from DA transfer.validUntil = auxData.validUntil; transfer.maxFee = auxData.maxFee == 0 ? transfer.fee : auxData.maxFee; // Validate require(ctx.timestamp < transfer.validUntil, "TRANSFER_EXPIRED"); require(transfer.fee <= transfer.maxFee, "TRANSFER_FEE_TOO_HIGH"); // Calculate the tx hash bytes32 txHash = hashTx(ctx.DOMAIN_SEPARATOR, transfer); // Check the on-chain authorization S.requireAuthorizedTx(transfer.from, auxData.signature, txHash); } function readTx( bytes memory data, uint offset, Transfer memory transfer ) internal pure { uint _offset = offset; require(data.toUint8Unsafe(_offset) == uint8(ExchangeData.TransactionType.TRANSFER), "INVALID_TX_TYPE"); _offset += 1; // Check that this is a conditional transfer require(data.toUint8Unsafe(_offset) == 1, "INVALID_AUXILIARYDATA_DATA"); _offset += 1; // Extract the transfer data // We don't use abi.decode for this because of the large amount of zero-padding // bytes the circuit would also have to hash. transfer.fromAccountID = data.toUint32Unsafe(_offset); _offset += 4; transfer.toAccountID = data.toUint32Unsafe(_offset); _offset += 4; transfer.tokenID = data.toUint16Unsafe(_offset); _offset += 2; transfer.amount = data.toUint24Unsafe(_offset).decodeFloat24(); _offset += 3; transfer.feeTokenID = data.toUint16Unsafe(_offset); _offset += 2; transfer.fee = data.toUint16Unsafe(_offset).decodeFloat16(); _offset += 2; transfer.storageID = data.toUint32Unsafe(_offset); _offset += 4; transfer.to = data.toAddressUnsafe(_offset); _offset += 20; transfer.from = data.toAddressUnsafe(_offset); _offset += 20; } function hashTx( bytes32 DOMAIN_SEPARATOR, Transfer memory transfer ) internal pure returns (bytes32) { return EIP712.hashPacked( DOMAIN_SEPARATOR, keccak256( abi.encode( TRANSFER_TYPEHASH, transfer.from, transfer.to, transfer.tokenID, transfer.amount, transfer.feeTokenID, transfer.maxFee, transfer.validUntil, transfer.storageID ) ) ); } } // File: contracts/core/iface/IL2MintableNFT.sol // Copyright 2017 Loopring Technology Limited. interface IL2MintableNFT { /// @dev This function is called when an NFT minted on L2 is withdrawn from Loopring. /// That means the NFTs were burned on L2 and now need to be minted on L1. /// /// This function can only be called by the Loopring exchange. /// /// @param to The owner of the NFT /// @param tokenId The token type 'id` /// @param amount The amount of NFTs to mint /// @param minter The minter on L2, which can be used to decide if the NFT is authentic /// @param data Opaque data that can be used by the contract function mintFromL2( address to, uint256 tokenId, uint amount, address minter, bytes calldata data ) external; /// @dev Returns a list of all address that are authorized to mint NFTs on L2. /// @return The list of authorized minter on L2 function minters() external view returns (address[] memory); } // File: contracts/thirdparty/erc165/IERC165.sol /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } // File: contracts/thirdparty/erc165/ERC165.sol /** * @dev Implementation of the {IERC165} interface. * * Contracts may inherit from this and call {_registerInterface} to declare * their support of an interface. */ abstract contract ERC165 is IERC165 { /* * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 */ bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; /** * @dev Mapping of interface ids to whether or not it's supported. */ mapping(bytes4 => bool) private _supportedInterfaces; constructor () { // Derived contracts need only register support for their own interfaces, // we register support for ERC165 itself here _registerInterface(_INTERFACE_ID_ERC165); } /** * @dev See {IERC165-supportsInterface}. * * Time complexity O(1), guaranteed to always use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) public view override returns (bool) { return _supportedInterfaces[interfaceId]; } /** * @dev Registers the contract as an implementer of the interface defined by * `interfaceId`. Support of the actual ERC165 interface is automatic and * registering its interface id is not required. * * See {IERC165-supportsInterface}. * * Requirements: * * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). */ function _registerInterface(bytes4 interfaceId) internal virtual { require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); _supportedInterfaces[interfaceId] = true; } } // File: contracts/thirdparty/erc1155/IERC1155.sol /** * @dev Required interface of an ERC1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[EIP]. * * _Available since v3.1._ */ interface IERC1155 is IERC165 { /** * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the amount of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the caller. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external; } // File: contracts/thirdparty/erc721/IERC721.sol /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; } // File: contracts/core/impl/libexchange/ExchangeNFT.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeNFT /// @author Brecht Devos - <[email protected]> library ExchangeNFT { using ExchangeNFT for ExchangeData.State; function deposit( ExchangeData.State storage S, address from, ExchangeData.NftType nftType, address token, uint256 nftID, uint amount, bytes memory extraData ) internal { if (amount == 0) { return; } // Disable calls to certain contracts require(S.isTokenAddressAllowed(token), "TOKEN_ADDRESS_NOT_ALLOWED"); if (nftType == ExchangeData.NftType.ERC1155) { IERC1155(token).safeTransferFrom( from, address(this), nftID, amount, extraData ); } else if (nftType == ExchangeData.NftType.ERC721) { require(amount == 1, "INVALID_AMOUNT"); IERC721(token).safeTransferFrom( from, address(this), nftID, extraData ); } else { revert("UNKNOWN_NFTTYPE"); } } function withdraw( ExchangeData.State storage S, address /*from*/, address to, ExchangeData.NftType nftType, address token, uint256 nftID, uint amount, bytes memory extraData, uint gasLimit ) internal returns (bool success) { if (amount == 0) { return true; } // Disable calls to certain contracts if(!S.isTokenAddressAllowed(token)) { return false; } if (nftType == ExchangeData.NftType.ERC1155) { try IERC1155(token).safeTransferFrom{gas: gasLimit}( address(this), to, nftID, amount, extraData ) { success = true; } catch { success = false; } } else if (nftType == ExchangeData.NftType.ERC721) { try IERC721(token).safeTransferFrom{gas: gasLimit}( address(this), to, nftID, extraData ) { success = true; } catch { success = false; } } else { revert("UNKNOWN_NFTTYPE"); } } function mintFromL2( ExchangeData.State storage S, address to, address token, uint256 nftID, uint amount, address minter, bytes memory extraData, uint gasLimit ) internal returns (bool success) { if (amount == 0) { return true; } // Disable calls to certain contracts if(!S.isTokenAddressAllowed(token)) { return false; } try IL2MintableNFT(token).mintFromL2{gas: gasLimit}( to, nftID, amount, minter, extraData ) { success = true; } catch { success = false; } } function isTokenAddressAllowed( ExchangeData.State storage S, address token ) internal view returns (bool valid) { return (token != address(this) && token != address(S.depositContract)); } } // File: contracts/core/impl/libexchange/ExchangeWithdrawals.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeWithdrawals. /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> library ExchangeWithdrawals { enum WithdrawalCategory { DISTRIBUTION, FROM_MERKLE_TREE, FROM_DEPOSIT_REQUEST, FROM_APPROVED_WITHDRAWAL } using AddressUtil for address; using AddressUtil for address payable; using BytesUtil for bytes; using MathUint for uint; using ExchangeBalances for ExchangeData.State; using ExchangeMode for ExchangeData.State; using ExchangeTokens for ExchangeData.State; using ExchangeTokens for uint16; event ForcedWithdrawalRequested( address owner, uint16 tokenID, // ERC20 token ID ( if < NFT_TOKEN_ID_START) or // NFT balance slot (if >= NFT_TOKEN_ID_START) uint32 accountID ); event WithdrawalCompleted( uint8 category, address from, address to, address token, uint amount ); event WithdrawalFailed( uint8 category, address from, address to, address token, uint amount ); event NftWithdrawalCompleted( uint8 category, address from, address to, uint16 tokenID, address token, uint256 nftID, uint amount ); event NftWithdrawalFailed( uint8 category, address from, address to, uint16 tokenID, address token, uint256 nftID, uint amount ); function forceWithdraw( ExchangeData.State storage S, address owner, uint16 tokenID, // ERC20 token ID ( if < NFT_TOKEN_ID_START) or // NFT balance slot (if >= NFT_TOKEN_ID_START) uint32 accountID ) public { require(!S.isInWithdrawalMode(), "INVALID_MODE"); // Limit the amount of pending forced withdrawals so that the owner cannot be overwhelmed. require(S.getNumAvailableForcedSlots() > 0, "TOO_MANY_REQUESTS_OPEN"); require(accountID < ExchangeData.MAX_NUM_ACCOUNTS, "INVALID_ACCOUNTID"); // Only allow withdrawing from registered ERC20 tokens or NFT tokenIDs require( tokenID < S.tokens.length || // ERC20 tokenID.isNFT(), // NFT "INVALID_TOKENID" ); // A user needs to pay a fixed ETH withdrawal fee, set by the protocol. uint withdrawalFeeETH = S.loopring.forcedWithdrawalFee(); // Check ETH value sent, can be larger than the expected withdraw fee require(msg.value >= withdrawalFeeETH, "INSUFFICIENT_FEE"); // Send surplus of ETH back to the sender uint feeSurplus = msg.value.sub(withdrawalFeeETH); if (feeSurplus > 0) { msg.sender.sendETHAndVerify(feeSurplus, gasleft()); } // There can only be a single forced withdrawal per (account, token) pair. require( S.pendingForcedWithdrawals[accountID][tokenID].timestamp == 0, "WITHDRAWAL_ALREADY_PENDING" ); // Store the forced withdrawal request data S.pendingForcedWithdrawals[accountID][tokenID] = ExchangeData.ForcedWithdrawal({ owner: owner, timestamp: uint64(block.timestamp) }); // Increment the number of pending forced transactions so we can keep count. S.numPendingForcedTransactions++; emit ForcedWithdrawalRequested( owner, tokenID, accountID ); } // We alow anyone to withdraw these funds for the account owner function withdrawFromMerkleTree( ExchangeData.State storage S, ExchangeData.MerkleProof calldata merkleProof ) public { require(S.isInWithdrawalMode(), "NOT_IN_WITHDRAW_MODE"); address owner = merkleProof.accountLeaf.owner; uint32 accountID = merkleProof.accountLeaf.accountID; uint16 tokenID = merkleProof.balanceLeaf.tokenID; uint96 balance = merkleProof.balanceLeaf.balance; // Make sure the funds aren't withdrawn already. require(S.withdrawnInWithdrawMode[accountID][tokenID] == false, "WITHDRAWN_ALREADY"); // Verify that the provided Merkle tree data is valid by using the Merkle proof. ExchangeBalances.verifyAccountBalance( uint(S.merkleRoot), merkleProof ); // Make sure the balance can only be withdrawn once S.withdrawnInWithdrawMode[accountID][tokenID] = true; if (!tokenID.isNFT()) { require( merkleProof.nft.nftID == 0 && merkleProof.nft.minter == address(0), "NOT_AN_NFT" ); // Transfer the tokens to the account owner transferTokens( S, uint8(WithdrawalCategory.FROM_MERKLE_TREE), owner, owner, tokenID, balance, new bytes(0), gasleft(), false ); } else { transferNFTs( S, uint8(WithdrawalCategory.DISTRIBUTION), owner, owner, tokenID, balance, merkleProof.nft, new bytes(0), gasleft(), false ); } } function withdrawFromDepositRequest( ExchangeData.State storage S, address owner, address token ) public { uint16 tokenID = S.getTokenID(token); ExchangeData.Deposit storage deposit = S.pendingDeposits[owner][tokenID]; require(deposit.timestamp != 0, "DEPOSIT_NOT_WITHDRAWABLE_YET"); // Check if the deposit has indeed exceeded the time limit of if the exchange is in withdrawal mode require( block.timestamp >= deposit.timestamp + S.maxAgeDepositUntilWithdrawable || S.isInWithdrawalMode(), "DEPOSIT_NOT_WITHDRAWABLE_YET" ); uint amount = deposit.amount; // Reset the deposit request delete S.pendingDeposits[owner][tokenID]; // Transfer the tokens transferTokens( S, uint8(WithdrawalCategory.FROM_DEPOSIT_REQUEST), owner, owner, tokenID, amount, new bytes(0), gasleft(), false ); } function withdrawFromNFTDepositRequest( ExchangeData.State storage S, address owner, address token, ExchangeData.NftType nftType, uint256 nftID ) public { ExchangeData.Deposit storage deposit = S.pendingNFTDeposits[owner][nftType][token][nftID]; require(deposit.timestamp != 0, "DEPOSIT_NOT_WITHDRAWABLE_YET"); // Check if the deposit has indeed exceeded the time limit of if the exchange is in withdrawal mode require( block.timestamp >= deposit.timestamp + S.maxAgeDepositUntilWithdrawable || S.isInWithdrawalMode(), "DEPOSIT_NOT_WITHDRAWABLE_YET" ); uint amount = deposit.amount; // Reset the deposit request delete S.pendingNFTDeposits[owner][nftType][token][nftID]; ExchangeData.Nft memory nft = ExchangeData.Nft({ minter: token, nftType: nftType, token: token, nftID: nftID, creatorFeeBips: 0 }); // Transfer the NFTs transferNFTs( S, uint8(WithdrawalCategory.FROM_DEPOSIT_REQUEST), owner, owner, 0, amount, nft, new bytes(0), gasleft(), false ); } function withdrawFromApprovedWithdrawals( ExchangeData.State storage S, address[] memory owners, address[] memory tokens ) public { require(owners.length == tokens.length, "INVALID_INPUT_DATA"); for (uint i = 0; i < owners.length; i++) { address owner = owners[i]; uint16 tokenID = S.getTokenID(tokens[i]); uint amount = S.amountWithdrawable[owner][tokenID]; // Make sure this amount can't be withdrawn again delete S.amountWithdrawable[owner][tokenID]; // Transfer the tokens to the owner transferTokens( S, uint8(WithdrawalCategory.FROM_APPROVED_WITHDRAWAL), owner, owner, tokenID, amount, new bytes(0), gasleft(), false ); } } function withdrawFromApprovedWithdrawalsNFT( ExchangeData.State storage S, address[] memory owners, address[] memory minters, ExchangeData.NftType[] memory nftTypes, address[] memory tokens, uint256[] memory nftIDs ) public { require(owners.length == minters.length, "INVALID_INPUT_DATA_MINTERS"); require(owners.length == nftTypes.length, "INVALID_INPUT_DATA_NFTTYPES"); require(owners.length == tokens.length, "INVALID_INPUT_DATA_TOKENS"); require(owners.length == nftIDs.length, "INVALID_INPUT_DATA_CONTENT_URIS"); for (uint i = 0; i < owners.length; i++) { address owner = owners[i]; address minter = minters[i]; ExchangeData.NftType nftType = nftTypes[i]; address token = tokens[i]; uint256 nftID = nftIDs[i]; uint amount = S.amountWithdrawableNFT[owner][minter][nftType][token][nftID]; // Make sure this amount can't be withdrawn again delete S.amountWithdrawableNFT[owner][minter][nftType][token][nftID]; ExchangeData.Nft memory nft = ExchangeData.Nft({ minter: minter, nftType: nftType, token: token, nftID: nftID, creatorFeeBips: 0 }); // Transfer the NFTs to the owner transferNFTs( S, uint8(WithdrawalCategory.DISTRIBUTION), owner, owner, 0, amount, nft, new bytes(0), gasleft(), false ); } } function distributeWithdrawal( ExchangeData.State storage S, address from, address to, uint16 tokenID, uint amount, bytes memory extraData, uint gasLimit, ExchangeData.Nft memory nft ) public { if (!tokenID.isNFT()) { // Try to transfer the tokens if (!transferTokens( S, uint8(WithdrawalCategory.DISTRIBUTION), from, to, tokenID, amount, extraData, gasLimit, true )) { // If the transfer was successful there's nothing left to do. // However, if the transfer failed the tokens are still in the contract and can be // withdrawn later to `to` by anyone by using `withdrawFromApprovedWithdrawal. S.amountWithdrawable[to][tokenID] = S.amountWithdrawable[to][tokenID].add(amount); } } else { // Try to transfer the tokens if (!transferNFTs( S, uint8(WithdrawalCategory.DISTRIBUTION), from, to, tokenID, amount, nft, extraData, gasLimit, true )) { // If the transfer was successful there's nothing left to do. // However, if the transfer failed the tokens are still in the contract and can be // withdrawn later to `to` by anyone by using `withdrawFromApprovedNftWithdrawal. S.amountWithdrawableNFT[to][nft.minter][nft.nftType][nft.token][nft.nftID] = S.amountWithdrawableNFT[to][nft.minter][nft.nftType][nft.token][nft.nftID].add(amount); } } } // == Internal and Private Functions == // If allowFailure is true the transfer can fail because of a transfer error or // because the transfer uses more than `gasLimit` gas. The function // will return true when successful, false otherwise. // If allowFailure is false the transfer is guaranteed to succeed using // as much gas as needed, otherwise it throws. The function always returns true. function transferTokens( ExchangeData.State storage S, uint8 category, address from, address to, uint16 tokenID, uint amount, bytes memory extraData, uint gasLimit, bool allowFailure ) private returns (bool success) { // Redirect withdrawals to address(0) to the protocol fee vault if (to == address(0)) { to = S.loopring.protocolFeeVault(); } address token = S.getTokenAddress(tokenID); // Transfer the tokens from the deposit contract to the owner if (gasLimit > 0) { try S.depositContract.withdraw{gas: gasLimit}(from, to, token, amount, extraData) { success = true; } catch { success = false; } } else { success = false; } require(allowFailure || success, "TRANSFER_FAILURE"); if (success) { emit WithdrawalCompleted(category, from, to, token, amount); // Keep track of when the protocol fees were last withdrawn // (only done to make this data easier available). if (from == address(0)) { S.protocolFeeLastWithdrawnTime[token] = block.timestamp; } } else { emit WithdrawalFailed(category, from, to, token, amount); } } // If allowFailure is true the transfer can fail because of a transfer error or // because the transfer uses more than `gasLimit` gas. The function // will return true when successful, false otherwise. // If allowFailure is false the transfer is guaranteed to succeed using // as much gas as needed, otherwise it throws. The function always returns true. function transferNFTs( ExchangeData.State storage S, uint8 category, address from, address to, uint16 tokenID, uint amount, ExchangeData.Nft memory nft, bytes memory extraData, uint gasLimit, bool allowFailure ) private returns (bool success) { if (nft.token == nft.minter) { // This is an existing thirdparty NFT contract success = ExchangeNFT.withdraw( S, from, to, nft.nftType, nft.token, nft.nftID, amount, extraData, gasLimit ); } else { // This is an inhouse NFT contract with L2 minting support success = ExchangeNFT.mintFromL2( S, to, nft.token, nft.nftID, amount, nft.minter, extraData, gasLimit ); } require(allowFailure || success, "NFT_TRANSFER_FAILURE"); if (success) { emit NftWithdrawalCompleted(category, from, to, tokenID, nft.token, nft.nftID, amount); } else { emit NftWithdrawalFailed(category, from, to, tokenID, nft.token, nft.nftID, amount); } } } // File: contracts/core/impl/libtransactions/NftDataTransaction.sol // Copyright 2017 Loopring Technology Limited. /// @title NftDataTransaction /// @author Brecht Devos - <[email protected]> library NftDataTransaction { using BlockReader for bytes; using BytesUtil for bytes; // Read the data in two transactions, each containing partial data. // Each tx contains largely the same data (`nftID`, `nftType`, `creatorFeeBips`) // except when // type == SCHEME_WITH_TOKEN_ADDRESS -> bring `tokenAddress` to L1, // type == SCHEME_WITH_MINTER_ADDRESS -> bring `minter` to L1. enum NftDataScheme { SCHEME_WITH_MINTER_ADDRESS, SCHEME_WITH_TOKEN_ADDRESS } struct NftData { uint8 scheme; uint32 accountID; // the `to` or `from` account's ID. uint16 tokenID; ExchangeData.Nft nft; } function readTx( bytes memory data, uint offset, NftData memory nftData ) internal pure { uint _offset = offset; require( data.toUint8Unsafe(_offset) == uint8(ExchangeData.TransactionType.NFT_DATA), "INVALID_TX_TYPE" ); _offset += 1; nftData.scheme = data.toUint8Unsafe(_offset); _offset += 1; // Extract the transfer data // We don't use abi.decode for this because of the large amount of zero-padding // bytes the circuit would also have to hash. nftData.accountID = data.toUint32Unsafe(_offset); _offset += 4; nftData.tokenID = data.toUint16Unsafe(_offset); _offset += 2; nftData.nft.nftID = data.toUintUnsafe(_offset); _offset += 32; nftData.nft.creatorFeeBips = data.toUint8Unsafe(_offset); _offset += 1; nftData.nft.nftType = ExchangeData.NftType(data.toUint8Unsafe(_offset)); _offset += 1; if (nftData.scheme == uint8(NftDataScheme.SCHEME_WITH_MINTER_ADDRESS)) { nftData.nft.minter = data.toAddressUnsafe(_offset); } else if (nftData.scheme == uint8(NftDataScheme.SCHEME_WITH_TOKEN_ADDRESS)) { nftData.nft.token = data.toAddressUnsafe(_offset); } else { revert("INVALID_NFT_DATA_SUBTYPE"); } _offset += 20; } function readDualNftData( ExchangeData.BlockContext memory ctx, uint32 accountID, uint16 tokenID, uint txIdx, NftDataTransaction.NftData memory nftData ) internal pure { // There's 68 bytes we can use per transaction. The NFT data now contains // `hash(minter, nftType, tokenAddress, nftID, creatorFeeBips)`. So this data // needs txType + (1 byte) + minter (20 bytes) + nftType (1 byte) + // tokenAddress (20 bytes) + nftID (32 bytes) + creatorFeeBips (1 byte) = 76 bytes. // So 8 bytes too much to fit inside the available space in a single tx. readNftData( ctx, accountID, tokenID, txIdx, NftDataScheme.SCHEME_WITH_MINTER_ADDRESS, nftData ); readNftData( ctx, accountID, tokenID, txIdx + 1, NftDataScheme.SCHEME_WITH_TOKEN_ADDRESS, nftData ); } function readNftData( ExchangeData.BlockContext memory ctx, uint32 accountID, uint16 tokenID, uint txOffset, NftDataScheme expectedScheme, NftDataTransaction.NftData memory nftData ) private pure { // Read the NFT_DATA transaction bytes memory txData = new bytes(ExchangeData.TX_DATA_AVAILABILITY_SIZE); ctx.block.data.readTransactionData(txOffset, ctx.block.blockSize, txData); NftDataTransaction.readTx(txData, 0, nftData); // Make sure the NFT_DATA transaction pushes data on-chain // that matches the the tokens that are getting withdrawn require( nftData.scheme == uint8(expectedScheme) && nftData.accountID == accountID && nftData.tokenID == tokenID, "INVALID_NFT_DATA" ); } } // File: contracts/core/impl/libtransactions/WithdrawTransaction.sol // Copyright 2017 Loopring Technology Limited. /// @title WithdrawTransaction /// @author Brecht Devos - <[email protected]> /// @dev The following 4 types of withdrawals are supported: /// - withdrawType = 0: offchain withdrawals with EdDSA signatures /// - withdrawType = 1: offchain withdrawals with ECDSA signatures or onchain appprovals /// - withdrawType = 2: onchain valid forced withdrawals (owner and accountID match), or /// offchain operator-initiated withdrawals for protocol fees or for /// users in shutdown mode /// - withdrawType = 3: onchain invalid forced withdrawals (owner and accountID mismatch) library WithdrawTransaction { using BlockReader for bytes; using BytesUtil for bytes; using FloatUtil for uint16; using MathUint for uint; using ExchangeMode for ExchangeData.State; using ExchangeSignatures for ExchangeData.State; using ExchangeTokens for uint16; using ExchangeWithdrawals for ExchangeData.State; bytes32 constant public WITHDRAWAL_TYPEHASH = keccak256( "Withdrawal(address owner,uint32 accountID,uint16 tokenID,uint96 amount,uint16 feeTokenID,uint96 maxFee,address to,bytes extraData,uint256 minGas,uint32 validUntil,uint32 storageID)" ); struct Withdrawal { uint withdrawalType; address from; uint32 fromAccountID; uint16 tokenID; uint96 amount; uint16 feeTokenID; uint96 maxFee; uint96 fee; address to; bytes extraData; uint minGas; uint32 validUntil; uint32 storageID; bytes20 onchainDataHash; } // Auxiliary data for each withdrawal struct WithdrawalAuxiliaryData { bool storeRecipient; uint gasLimit; bytes signature; uint minGas; address to; bytes extraData; uint96 maxFee; uint32 validUntil; } function process( ExchangeData.State storage S, ExchangeData.BlockContext memory ctx, bytes memory data, uint offset, bytes memory auxiliaryData ) internal { Withdrawal memory withdrawal; readTx(data, offset, withdrawal); // Read the NFT data if we're withdrawing an NFT NftDataTransaction.NftData memory nftData; if (withdrawal.tokenID.isNFT() && withdrawal.amount > 0) { NftDataTransaction.readDualNftData( ctx, withdrawal.fromAccountID, withdrawal.tokenID, ctx.txIndex.sub(2), nftData ); } WithdrawalAuxiliaryData memory auxData = abi.decode(auxiliaryData, (WithdrawalAuxiliaryData)); // Validate the withdrawal data not directly part of the DA bytes20 onchainDataHash = hashOnchainData( auxData.minGas, auxData.to, auxData.extraData ); // Only the 20 MSB are used, which is still 80-bit of security, which is more // than enough, especially when combined with validUntil. require(withdrawal.onchainDataHash == onchainDataHash, "INVALID_WITHDRAWAL_DATA"); // Fill in withdrawal data missing from DA withdrawal.to = auxData.to; withdrawal.minGas = auxData.minGas; withdrawal.extraData = auxData.extraData; withdrawal.maxFee = auxData.maxFee == 0 ? withdrawal.fee : auxData.maxFee; withdrawal.validUntil = auxData.validUntil; // If the account has an owner, don't allow withdrawing to the zero address // (which will be the protocol fee vault contract). require(withdrawal.from == address(0) || withdrawal.to != address(0), "INVALID_WITHDRAWAL_RECIPIENT"); if (withdrawal.withdrawalType == 0) { // Signature checked offchain, nothing to do } else if (withdrawal.withdrawalType == 1) { // Validate require(ctx.timestamp < withdrawal.validUntil, "WITHDRAWAL_EXPIRED"); require(withdrawal.fee <= withdrawal.maxFee, "WITHDRAWAL_FEE_TOO_HIGH"); // Check appproval onchain // Calculate the tx hash bytes32 txHash = hashTx(ctx.DOMAIN_SEPARATOR, withdrawal); // Check onchain authorization S.requireAuthorizedTx(withdrawal.from, auxData.signature, txHash); } else if (withdrawal.withdrawalType == 2 || withdrawal.withdrawalType == 3) { // Forced withdrawals cannot make use of certain features because the // necessary data is not authorized by the account owner. // For protocol fee withdrawals, `owner` and `to` are both address(0). require(withdrawal.from == withdrawal.to, "INVALID_WITHDRAWAL_ADDRESS"); // Forced withdrawal fees are charged when the request is submitted. require(withdrawal.fee == 0, "FEE_NOT_ZERO"); require(withdrawal.extraData.length == 0, "AUXILIARY_DATA_NOT_ALLOWED"); ExchangeData.ForcedWithdrawal memory forcedWithdrawal = S.pendingForcedWithdrawals[withdrawal.fromAccountID][withdrawal.tokenID]; if (forcedWithdrawal.timestamp != 0) { if (withdrawal.withdrawalType == 2) { require(withdrawal.from == forcedWithdrawal.owner, "INCONSISENT_OWNER"); } else { //withdrawal.withdrawalType == 3 require(withdrawal.from != forcedWithdrawal.owner, "INCONSISENT_OWNER"); require(withdrawal.amount == 0, "UNAUTHORIZED_WITHDRAWAL"); } // delete the withdrawal request and free a slot delete S.pendingForcedWithdrawals[withdrawal.fromAccountID][withdrawal.tokenID]; S.numPendingForcedTransactions--; } else { // Allow the owner to submit full withdrawals without authorization // - when in shutdown mode // - to withdraw protocol fees require( withdrawal.fromAccountID == ExchangeData.ACCOUNTID_PROTOCOLFEE || S.isShutdown(), "FULL_WITHDRAWAL_UNAUTHORIZED" ); } } else { revert("INVALID_WITHDRAWAL_TYPE"); } // Check if there is a withdrawal recipient address recipient = S.withdrawalRecipient[withdrawal.from][withdrawal.to][withdrawal.tokenID][withdrawal.amount][withdrawal.storageID]; if (recipient != address(0)) { // Auxiliary data is not supported require (withdrawal.extraData.length == 0, "AUXILIARY_DATA_NOT_ALLOWED"); // Set the new recipient address withdrawal.to = recipient; // Allow any amount of gas to be used on this withdrawal (which allows the transfer to be skipped) withdrawal.minGas = 0; // Do NOT delete the recipient to prevent replay attack // delete S.withdrawalRecipient[withdrawal.owner][withdrawal.to][withdrawal.tokenID][withdrawal.amount][withdrawal.storageID]; } else if (auxData.storeRecipient) { // Store the destination address to mark the withdrawal as done require(withdrawal.to != address(0), "INVALID_DESTINATION_ADDRESS"); S.withdrawalRecipient[withdrawal.from][withdrawal.to][withdrawal.tokenID][withdrawal.amount][withdrawal.storageID] = withdrawal.to; } // Validate gas provided require(auxData.gasLimit >= withdrawal.minGas, "OUT_OF_GAS_FOR_WITHDRAWAL"); // Try to transfer the tokens with the provided gas limit S.distributeWithdrawal( withdrawal.from, withdrawal.to, withdrawal.tokenID, withdrawal.amount, withdrawal.extraData, auxData.gasLimit, nftData.nft ); } function readTx( bytes memory data, uint offset, Withdrawal memory withdrawal ) internal pure { uint _offset = offset; require(data.toUint8Unsafe(_offset) == uint8(ExchangeData.TransactionType.WITHDRAWAL), "INVALID_TX_TYPE"); _offset += 1; // Extract the transfer data // We don't use abi.decode for this because of the large amount of zero-padding // bytes the circuit would also have to hash. withdrawal.withdrawalType = data.toUint8Unsafe(_offset); _offset += 1; withdrawal.from = data.toAddressUnsafe(_offset); _offset += 20; withdrawal.fromAccountID = data.toUint32Unsafe(_offset); _offset += 4; withdrawal.tokenID = data.toUint16Unsafe(_offset); _offset += 2; withdrawal.amount = data.toUint96Unsafe(_offset); _offset += 12; withdrawal.feeTokenID = data.toUint16Unsafe(_offset); _offset += 2; withdrawal.fee = data.toUint16Unsafe(_offset).decodeFloat16(); _offset += 2; withdrawal.storageID = data.toUint32Unsafe(_offset); _offset += 4; withdrawal.onchainDataHash = data.toBytes20Unsafe(_offset); _offset += 20; } function hashTx( bytes32 DOMAIN_SEPARATOR, Withdrawal memory withdrawal ) internal pure returns (bytes32) { return EIP712.hashPacked( DOMAIN_SEPARATOR, keccak256( abi.encode( WITHDRAWAL_TYPEHASH, withdrawal.from, withdrawal.fromAccountID, withdrawal.tokenID, withdrawal.amount, withdrawal.feeTokenID, withdrawal.maxFee, withdrawal.to, keccak256(withdrawal.extraData), withdrawal.minGas, withdrawal.validUntil, withdrawal.storageID ) ) ); } function hashOnchainData( uint minGas, address to, bytes memory extraData ) internal pure returns (bytes20) { // Only the 20 MSB are used, which is still 80-bit of security, which is more // than enough, especially when combined with validUntil. return bytes20(keccak256( abi.encodePacked( minGas, to, extraData ) )); } } // File: contracts/core/impl/libtransactions/NftMintTransaction.sol // Copyright 2017 Loopring Technology Limited. /// @title NftMintTransaction /// @author Brecht Devos - <[email protected]> library NftMintTransaction { using BlockReader for bytes; using BytesUtil for bytes; using ExchangeSignatures for ExchangeData.State; using FloatUtil for uint16; using MathUint96 for uint96; using MathUint for uint; bytes32 constant public NFTMINT_TYPEHASH = keccak256( "NftMint(address minter,address to,uint8 nftType,address token,uint256 nftID,uint8 creatorFeeBips,uint96 amount,uint16 feeTokenID,uint96 maxFee,uint32 validUntil,uint32 storageID)" ); // This structure represents either a L2 NFT mint or a L1-to-L2 NFT deposit. struct NftMint { uint mintType; uint32 minterAccountID; uint32 toAccountID; uint16 toTokenID; // slot uint96 amount; uint16 feeTokenID; uint96 maxFee; uint96 fee; uint32 validUntil; uint32 storageID; address to; ExchangeData.Nft nft; } // Auxiliary data for each NFT mint struct NftMintAuxiliaryData { bytes signature; uint96 maxFee; uint32 validUntil; } function process( ExchangeData.State storage S, ExchangeData.BlockContext memory ctx, bytes memory data, uint offset, bytes memory auxiliaryData ) internal { // Read in the mint NftMint memory mint; readTx(data, offset, mint); // Read the NFT data if we're minting or depositing an NFT // // Note that EdDSA-based minting has the following restrictions due // to storage limit: // 1) It's only possible to mint to the minter's own account. // 2) The max amount that can be minted is limited to 65535 (2**16 - 1) per mint. // // ECDSA and onchain approval hash-based minting do not have the above restrictions. { // Read the NFT data NftDataTransaction.NftData memory nftData; NftDataTransaction.readDualNftData( ctx, mint.toAccountID, mint.toTokenID, ctx.txIndex.add(1), nftData ); // Copy the data to the mint struct mint.nft = nftData.nft; } if (mint.mintType == 2) { // No fee allowed for deposits require(mint.fee == 0, "DEPOSIT_FEE_DISALLOWED"); require(mint.nft.creatorFeeBips == 0, "CREATORFEEBIPS_NONZERO"); // The minter should be the NFT token contract for deposits require(mint.nft.minter == mint.nft.token, "MINTER_NOT_TOKEN_CONTRACT"); // Process the deposit ExchangeData.Deposit memory pendingDeposit = S.pendingNFTDeposits[mint.to][mint.nft.nftType][mint.nft.token][mint.nft.nftID]; // Make sure the deposit was actually done require(pendingDeposit.timestamp > 0, "DEPOSIT_NOT_EXIST"); // Processing partial amounts of the deposited amount is allowed. // This is done to ensure the user can do multiple deposits after each other // without invalidating work done by the exchange owner for previous deposit amounts. require(pendingDeposit.amount >= mint.amount, "INVALID_AMOUNT"); pendingDeposit.amount = pendingDeposit.amount.sub(mint.amount); // If the deposit was fully consumed, reset it so the storage is freed up // and the owner receives a gas refund. if (pendingDeposit.amount == 0) { delete S.pendingNFTDeposits[mint.to][mint.nft.nftType][mint.nft.token][mint.nft.nftID]; } else { S.pendingNFTDeposits[mint.to][mint.nft.nftType][mint.nft.token][mint.nft.nftID] = pendingDeposit; } } else { // The minter should NOT be the NFT token contract for L2 mints require(mint.nft.minter != mint.nft.token, "MINTER_EQUALS_TOKEN_CONTRACT"); NftMintAuxiliaryData memory auxData = abi.decode(auxiliaryData, (NftMintAuxiliaryData)); // Fill in withdrawal data missing from DA or only available in the NftData // Fill in withdrawal data missing from DA mint.validUntil = auxData.validUntil; mint.maxFee = auxData.maxFee == 0 ? mint.fee : auxData.maxFee; // Validate require(ctx.timestamp < mint.validUntil, "NFTMINT_EXPIRED"); require(mint.fee <= mint.maxFee, "NFTMINT_FEE_TOO_HIGH"); // Calculate the tx hash bytes32 txHash = hashTx(ctx.DOMAIN_SEPARATOR, mint); // Check the on-chain authorization S.requireAuthorizedTx(mint.nft.minter, auxData.signature, txHash); } } function readTx( bytes memory data, uint offset, NftMint memory mint ) internal pure { uint _offset = offset; require( data.toUint8Unsafe(_offset) == uint8(ExchangeData.TransactionType.NFT_MINT), "INVALID_TX_TYPE" ); _offset += 1; mint.mintType = data.toUint8Unsafe(_offset); _offset += 1; // Check that this is a conditional mint require(mint.mintType > 0, "INVALID_AUXILIARY_DATA"); // We don't use abi.decode for this because of the large amount of zero-padding // bytes the circuit would also have to hash. mint.minterAccountID = data.toUint32Unsafe(_offset); _offset += 4; mint.toTokenID = data.toUint16Unsafe(_offset); _offset += 2; mint.feeTokenID = data.toUint16Unsafe(_offset); _offset += 2; mint.fee = data.toUint16Unsafe(_offset).decodeFloat16(); _offset += 2; mint.amount = data.toUint96Unsafe(_offset); _offset += 12; mint.storageID = data.toUint32Unsafe(_offset); _offset += 4; mint.toAccountID = data.toUint32Unsafe(_offset); _offset += 4; mint.to = data.toAddressUnsafe(_offset); _offset += 20; } function hashTx( bytes32 DOMAIN_SEPARATOR, NftMint memory mint ) internal pure returns (bytes32) { return EIP712.hashPacked( DOMAIN_SEPARATOR, keccak256( abi.encode( NFTMINT_TYPEHASH, mint.nft.minter, mint.to, mint.nft.nftType, mint.nft.token, mint.nft.nftID, mint.nft.creatorFeeBips, mint.amount, mint.feeTokenID, mint.maxFee, mint.validUntil, mint.storageID ) ) ); } } // File: contracts/core/impl/libexchange/ExchangeBlocks.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeBlocks. /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> library ExchangeBlocks { using AddressUtil for address; using AddressUtil for address payable; using BlockReader for bytes; using BytesUtil for bytes; using MathUint for uint; using ExchangeMode for ExchangeData.State; using ExchangeWithdrawals for ExchangeData.State; using SignatureUtil for bytes32; event BlockSubmitted( uint indexed blockIdx, bytes32 merkleRoot, bytes32 publicDataHash ); event ProtocolFeesUpdated( uint8 takerFeeBips, uint8 makerFeeBips, uint8 previousTakerFeeBips, uint8 previousMakerFeeBips ); function submitBlocks( ExchangeData.State storage S, ExchangeData.Block[] memory blocks ) public { // Exchange cannot be in withdrawal mode require(!S.isInWithdrawalMode(), "INVALID_MODE"); // Commit the blocks bytes32[] memory publicDataHashes = new bytes32[](blocks.length); for (uint i = 0; i < blocks.length; i++) { // Hash all the public data to a single value which is used as the input for the circuit publicDataHashes[i] = blocks[i].data.fastSHA256(); // Commit the block commitBlock(S, blocks[i], publicDataHashes[i]); } // Verify the blocks - blocks are verified in a batch to save gas. verifyBlocks(S, blocks, publicDataHashes); } // == Internal Functions == function commitBlock( ExchangeData.State storage S, ExchangeData.Block memory _block, bytes32 _publicDataHash ) private { // Read the block header BlockReader.BlockHeader memory header = _block.data.readHeader(); // Validate the exchange require(header.exchange == address(this), "INVALID_EXCHANGE"); // Validate the Merkle roots require(header.merkleRootBefore == S.merkleRoot, "INVALID_MERKLE_ROOT"); require(header.merkleRootAfter != header.merkleRootBefore, "EMPTY_BLOCK_DISABLED"); require(uint(header.merkleRootAfter) < ExchangeData.SNARK_SCALAR_FIELD, "INVALID_MERKLE_ROOT"); // Validate the timestamp require( header.timestamp > block.timestamp - ExchangeData.TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS && header.timestamp < block.timestamp + ExchangeData.TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS, "INVALID_TIMESTAMP" ); // Validate the protocol fee values require( validateAndSyncProtocolFees(S, header.protocolTakerFeeBips, header.protocolMakerFeeBips), "INVALID_PROTOCOL_FEES" ); // Process conditional transactions processConditionalTransactions( S, _block, header ); // Emit an event uint numBlocks = S.numBlocks; emit BlockSubmitted(numBlocks, header.merkleRootAfter, _publicDataHash); S.merkleRoot = header.merkleRootAfter; if (_block.storeBlockInfoOnchain) { S.blocks[numBlocks] = ExchangeData.BlockInfo( uint32(block.timestamp), bytes28(_publicDataHash) ); } S.numBlocks = numBlocks + 1; } function verifyBlocks( ExchangeData.State storage S, ExchangeData.Block[] memory blocks, bytes32[] memory publicDataHashes ) private view { IBlockVerifier blockVerifier = S.blockVerifier; uint numBlocksVerified = 0; bool[] memory blockVerified = new bool[](blocks.length); ExchangeData.Block memory firstBlock; uint[] memory batch = new uint[](blocks.length); while (numBlocksVerified < blocks.length) { // Find all blocks of the same type uint batchLength = 0; for (uint i = 0; i < blocks.length; i++) { if (blockVerified[i] == false) { if (batchLength == 0) { firstBlock = blocks[i]; batch[batchLength++] = i; } else { ExchangeData.Block memory _block = blocks[i]; if (_block.blockType == firstBlock.blockType && _block.blockSize == firstBlock.blockSize && _block.blockVersion == firstBlock.blockVersion) { batch[batchLength++] = i; } } } } // Prepare the data for batch verification uint[] memory publicInputs = new uint[](batchLength); uint[] memory proofs = new uint[](batchLength * 8); for (uint i = 0; i < batchLength; i++) { uint blockIdx = batch[i]; // Mark the block as verified blockVerified[blockIdx] = true; // Strip the 3 least significant bits of the public data hash // so we don't have any overflow in the snark field publicInputs[i] = uint(publicDataHashes[blockIdx]) >> 3; // Copy proof ExchangeData.Block memory _block = blocks[blockIdx]; for (uint j = 0; j < 8; j++) { proofs[i*8 + j] = _block.proof[j]; } } // Verify the proofs require( blockVerifier.verifyProofs( uint8(firstBlock.blockType), firstBlock.blockSize, firstBlock.blockVersion, publicInputs, proofs ), "INVALID_PROOF" ); numBlocksVerified += batchLength; } } function processConditionalTransactions( ExchangeData.State storage S, ExchangeData.Block memory _block, BlockReader.BlockHeader memory header ) private { if (header.numConditionalTransactions > 0) { // Cache the domain separator to save on SLOADs each time it is accessed. ExchangeData.BlockContext memory ctx = ExchangeData.BlockContext({ DOMAIN_SEPARATOR: S.DOMAIN_SEPARATOR, timestamp: header.timestamp, block: _block, txIndex: 0 }); ExchangeData.AuxiliaryData[] memory block_auxiliaryData; bytes memory blockAuxData = _block.auxiliaryData; assembly { block_auxiliaryData := add(blockAuxData, 64) } require( block_auxiliaryData.length == header.numConditionalTransactions, "AUXILIARYDATA_INVALID_LENGTH" ); // Run over all conditional transactions uint minTxIndex = 0; bytes memory txData = new bytes(ExchangeData.TX_DATA_AVAILABILITY_SIZE); for (uint i = 0; i < block_auxiliaryData.length; i++) { // Load the data from auxiliaryData, which is still encoded as calldata uint txIndex; bool approved; bytes memory auxData; assembly { // Offset to block_auxiliaryData[i] let auxOffset := mload(add(block_auxiliaryData, add(32, mul(32, i)))) // Load `txIndex` (pos 0) and `approved` (pos 1) in block_auxiliaryData[i] txIndex := mload(add(add(32, block_auxiliaryData), auxOffset)) approved := mload(add(add(64, block_auxiliaryData), auxOffset)) // Load `data` (pos 2) let auxDataOffset := mload(add(add(96, block_auxiliaryData), auxOffset)) auxData := add(add(32, block_auxiliaryData), add(auxOffset, auxDataOffset)) } ctx.txIndex = txIndex; // Each conditional transaction needs to be processed from left to right require(txIndex >= minTxIndex, "AUXILIARYDATA_INVALID_ORDER"); minTxIndex = txIndex + 1; if (approved) { continue; } // Get the transaction data _block.data.readTransactionData(txIndex, _block.blockSize, txData); // Process the transaction ExchangeData.TransactionType txType = ExchangeData.TransactionType( txData.toUint8(0) ); uint txDataOffset = 0; if (txType == ExchangeData.TransactionType.DEPOSIT) { DepositTransaction.process( S, ctx, txData, txDataOffset, auxData ); } else if (txType == ExchangeData.TransactionType.WITHDRAWAL) { WithdrawTransaction.process( S, ctx, txData, txDataOffset, auxData ); } else if (txType == ExchangeData.TransactionType.TRANSFER) { TransferTransaction.process( S, ctx, txData, txDataOffset, auxData ); } else if (txType == ExchangeData.TransactionType.ACCOUNT_UPDATE) { AccountUpdateTransaction.process( S, ctx, txData, txDataOffset, auxData ); } else if (txType == ExchangeData.TransactionType.AMM_UPDATE) { AmmUpdateTransaction.process( S, ctx, txData, txDataOffset, auxData ); } else if (txType == ExchangeData.TransactionType.NFT_MINT) { NftMintTransaction.process( S, ctx, txData, txDataOffset, auxData ); } else { // ExchangeData.TransactionType.NOOP, // ExchangeData.TransactionType.SPOT_TRADE and // ExchangeData.TransactionType.SIGNATURE_VERIFICATION // are not supported revert("UNSUPPORTED_TX_TYPE"); } } } } function validateAndSyncProtocolFees( ExchangeData.State storage S, uint8 takerFeeBips, uint8 makerFeeBips ) private returns (bool) { ExchangeData.ProtocolFeeData memory data = S.protocolFeeData; if (block.timestamp > data.syncedAt + ExchangeData.MIN_AGE_PROTOCOL_FEES_UNTIL_UPDATED) { // Store the current protocol fees in the previous protocol fees data.previousTakerFeeBips = data.takerFeeBips; data.previousMakerFeeBips = data.makerFeeBips; // Get the latest protocol fees for this exchange (data.takerFeeBips, data.makerFeeBips) = S.loopring.getProtocolFeeValues(); data.syncedAt = uint32(block.timestamp); if (data.takerFeeBips != data.previousTakerFeeBips || data.makerFeeBips != data.previousMakerFeeBips) { emit ProtocolFeesUpdated( data.takerFeeBips, data.makerFeeBips, data.previousTakerFeeBips, data.previousMakerFeeBips ); } // Update the data in storage S.protocolFeeData = data; } // The given fee values are valid if they are the current or previous protocol fee values return (takerFeeBips == data.takerFeeBips && makerFeeBips == data.makerFeeBips) || (takerFeeBips == data.previousTakerFeeBips && makerFeeBips == data.previousMakerFeeBips); } } // File: contracts/core/impl/libexchange/ExchangeDeposits.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeDeposits. /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library ExchangeDeposits { using AddressUtil for address payable; using MathUint96 for uint96; using ExchangeMode for ExchangeData.State; using ExchangeTokens for ExchangeData.State; event DepositRequested( address from, address to, address token, uint16 tokenId, uint96 amount ); event NFTDepositRequested( address from, address to, uint8 nftType, address token, uint256 nftID, uint96 amount ); function deposit( ExchangeData.State storage S, address from, address to, address tokenAddress, uint96 amount, // can be zero bytes memory extraData ) internal // inline call { require(to != address(0), "ZERO_ADDRESS"); // Deposits are still possible when the exchange is being shutdown, or even in withdrawal mode. // This is fine because the user can easily withdraw the deposited amounts again. // We don't want to make all deposits more expensive just to stop that from happening. // Allow depositing with amount == 0 to allow updating the deposit timestamp uint16 tokenID = S.getTokenID(tokenAddress); // Transfer the tokens to this contract uint96 amountDeposited = S.depositContract.deposit{value: msg.value}( from, tokenAddress, amount, extraData ); // Add the amount to the deposit request and reset the time the operator has to process it ExchangeData.Deposit memory _deposit = S.pendingDeposits[to][tokenID]; _deposit.timestamp = uint64(block.timestamp); _deposit.amount = _deposit.amount.add(amountDeposited); S.pendingDeposits[to][tokenID] = _deposit; emit DepositRequested( from, to, tokenAddress, tokenID, amountDeposited ); } function depositNFT( ExchangeData.State storage S, address from, address to, ExchangeData.NftType nftType, address tokenAddress, uint256 nftID, uint96 amount, // can be zero bytes memory extraData ) public { require(to != address(0), "ZERO_ADDRESS"); // Deposits are still possible when the exchange is being shutdown, or even in withdrawal mode. // This is fine because the user can easily withdraw the deposited amounts again. // We don't want to make all deposits more expensive just to stop that from happening. // Allow depositing with amount == 0 to allow updating the deposit timestamp // Transfer the tokens to this contract ExchangeNFT.deposit( S, from, nftType, tokenAddress, nftID, amount, extraData ); // Add the amount to the deposit request and reset the time the operator has to process it ExchangeData.Deposit memory _deposit = S.pendingNFTDeposits[to][nftType][tokenAddress][nftID]; _deposit.timestamp = uint64(block.timestamp); _deposit.amount = _deposit.amount.add(amount); S.pendingNFTDeposits[to][nftType][tokenAddress][nftID] = _deposit; emit NFTDepositRequested( from, to, uint8(nftType), tokenAddress, nftID, amount ); } } // File: contracts/core/impl/libexchange/ExchangeGenesis.sol // Copyright 2017 Loopring Technology Limited. /// @title ExchangeGenesis. /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library ExchangeGenesis { using ExchangeTokens for ExchangeData.State; function initializeGenesisBlock( ExchangeData.State storage S, address _loopringAddr, bytes32 _genesisMerkleRoot, bytes32 _domainSeparator ) public { require(address(0) != _loopringAddr, "INVALID_LOOPRING_ADDRESS"); require(_genesisMerkleRoot != 0, "INVALID_GENESIS_MERKLE_ROOT"); S.maxAgeDepositUntilWithdrawable = ExchangeData.MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE_UPPERBOUND; S.DOMAIN_SEPARATOR = _domainSeparator; ILoopringV3 loopring = ILoopringV3(_loopringAddr); S.loopring = loopring; S.blockVerifier = IBlockVerifier(loopring.blockVerifierAddress()); S.merkleRoot = _genesisMerkleRoot; S.blocks[0] = ExchangeData.BlockInfo(uint32(block.timestamp), bytes28(0)); S.numBlocks = 1; // Get the protocol fees for this exchange S.protocolFeeData.syncedAt = uint32(0); S.protocolFeeData.takerFeeBips = S.loopring.protocolTakerFeeBips(); S.protocolFeeData.makerFeeBips = S.loopring.protocolMakerFeeBips(); S.protocolFeeData.previousTakerFeeBips = S.protocolFeeData.takerFeeBips; S.protocolFeeData.previousMakerFeeBips = S.protocolFeeData.makerFeeBips; // Call these after the main state has been set up S.registerToken(address(0)); S.registerToken(loopring.lrcAddress()); } } // File: contracts/core/impl/ExchangeV3.sol // Copyright 2017 Loopring Technology Limited. /// @title An Implementation of IExchangeV3. /// @dev This contract supports upgradability proxy, therefore its constructor /// must do NOTHING. /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> contract ExchangeV3 is IExchangeV3, ReentrancyGuard, ERC1155Holder, ERC721Holder { using AddressUtil for address; using ERC20SafeTransfer for address; using MathUint for uint; using ExchangeAdmins for ExchangeData.State; using ExchangeBalances for ExchangeData.State; using ExchangeBlocks for ExchangeData.State; using ExchangeDeposits for ExchangeData.State; using ExchangeGenesis for ExchangeData.State; using ExchangeMode for ExchangeData.State; using ExchangeTokens for ExchangeData.State; using ExchangeWithdrawals for ExchangeData.State; ExchangeData.State private state; modifier onlyWhenUninitialized() { require( state.loopringAddr == address(0) && state.merkleRoot == bytes32(0), "INITIALIZED" ); _; } modifier onlyFromUserOrAgent(address owner) { require(isUserOrAgent(owner), "UNAUTHORIZED"); _; } /// @dev The constructor must do NOTHING to support proxy. constructor() {} function version() public pure returns (string memory) { return "3.6.0"; } function domainSeparator() public view returns (bytes32) { return state.DOMAIN_SEPARATOR; } // -- Initialization -- function initialize( address _loopring, address _owner, bytes32 _genesisMerkleRoot ) external override nonReentrant onlyWhenUninitialized { require(address(0) != _owner, "ZERO_ADDRESS"); owner = _owner; state.loopringAddr = _loopring; state.initializeGenesisBlock( _loopring, _genesisMerkleRoot, EIP712.hash(EIP712.Domain("Loopring Protocol", version(), address(this))) ); } function setAgentRegistry(address _agentRegistry) external override nonReentrant onlyOwner { require(_agentRegistry != address(0), "ZERO_ADDRESS"); require(state.agentRegistry == IAgentRegistry(0), "ALREADY_SET"); state.agentRegistry = IAgentRegistry(_agentRegistry); } function refreshBlockVerifier() external override nonReentrant onlyOwner { require(state.loopring.blockVerifierAddress() != address(0), "ZERO_ADDRESS"); state.blockVerifier = IBlockVerifier(state.loopring.blockVerifierAddress()); } function getAgentRegistry() external override view returns (IAgentRegistry) { return state.agentRegistry; } function setDepositContract(address _depositContract) external override nonReentrant onlyOwner { require(_depositContract != address(0), "ZERO_ADDRESS"); // Only used for initialization require(state.depositContract == IDepositContract(0), "ALREADY_SET"); state.depositContract = IDepositContract(_depositContract); } function getDepositContract() external override view returns (IDepositContract) { return state.depositContract; } function withdrawExchangeFees( address token, address recipient ) external override nonReentrant onlyOwner { require(recipient != address(0), "INVALID_ADDRESS"); // Does not call a standard NFT transfer function so we can allow any contract address. // Disallow calls to the deposit contract as a precaution. require(token != address(state.depositContract), "INVALID_TOKEN"); if (token == address(0)) { uint amount = address(this).balance; recipient.sendETHAndVerify(amount, gasleft()); } else { uint amount = ERC20(token).balanceOf(address(this)); token.safeTransferAndVerify(recipient, amount); } } function isUserOrAgent(address owner) public view returns (bool) { return owner == msg.sender || state.agentRegistry != IAgentRegistry(address(0)) && state.agentRegistry.isAgent(owner, msg.sender); } // -- Constants -- function getConstants() external override pure returns(ExchangeData.Constants memory) { return ExchangeData.Constants( uint(ExchangeData.SNARK_SCALAR_FIELD), uint(ExchangeData.MAX_OPEN_FORCED_REQUESTS), uint(ExchangeData.MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE), uint(ExchangeData.TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS), uint(ExchangeData.MAX_NUM_ACCOUNTS), uint(ExchangeData.MAX_NUM_TOKENS), uint(ExchangeData.MIN_AGE_PROTOCOL_FEES_UNTIL_UPDATED), uint(ExchangeData.MIN_TIME_IN_SHUTDOWN), uint(ExchangeData.TX_DATA_AVAILABILITY_SIZE), uint(ExchangeData.MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE_UPPERBOUND) ); } // -- Mode -- function isInWithdrawalMode() external override view returns (bool) { return state.isInWithdrawalMode(); } function isShutdown() external override view returns (bool) { return state.isShutdown(); } // -- Tokens -- function registerToken( address tokenAddress ) external override nonReentrant onlyOwner returns (uint16) { return state.registerToken(tokenAddress); } function getTokenID( address tokenAddress ) external override view returns (uint16) { return state.getTokenID(tokenAddress); } function getTokenAddress( uint16 tokenID ) external override view returns (address) { return state.getTokenAddress(tokenID); } // -- Stakes -- function getExchangeStake() external override view returns (uint) { return state.loopring.getExchangeStake(address(this)); } function withdrawExchangeStake( address recipient ) external override nonReentrant onlyOwner returns (uint) { return state.withdrawExchangeStake(recipient); } function getProtocolFeeLastWithdrawnTime( address tokenAddress ) external override view returns (uint) { return state.protocolFeeLastWithdrawnTime[tokenAddress]; } function burnExchangeStake() external override nonReentrant { // Allow burning the complete exchange stake when the exchange gets into withdrawal mode if (state.isInWithdrawalMode()) { // Burn the complete stake of the exchange uint stake = state.loopring.getExchangeStake(address(this)); state.loopring.burnExchangeStake(stake); } } // -- Blocks -- function getMerkleRoot() external override view returns (bytes32) { return state.merkleRoot; } function getBlockHeight() external override view returns (uint) { return state.numBlocks; } function getBlockInfo(uint blockIdx) external override view returns (ExchangeData.BlockInfo memory) { return state.blocks[blockIdx]; } function submitBlocks(ExchangeData.Block[] calldata blocks) external override nonReentrant onlyOwner { state.submitBlocks(blocks); } function getNumAvailableForcedSlots() external override view returns (uint) { return state.getNumAvailableForcedSlots(); } // -- Deposits -- function deposit( address from, address to, address tokenAddress, uint96 amount, bytes calldata extraData ) external payable override nonReentrant onlyFromUserOrAgent(from) { state.deposit(from, to, tokenAddress, amount, extraData); } function depositNFT( address from, address to, ExchangeData.NftType nftType, address tokenAddress, uint256 nftID, uint96 amount, bytes calldata extraData ) external override nonReentrant onlyFromUserOrAgent(from) { state.depositNFT(from, to, nftType, tokenAddress, nftID, amount, extraData); } function getPendingDepositAmount( address owner, address tokenAddress ) public override view returns (uint96) { uint16 tokenID = state.getTokenID(tokenAddress); return state.pendingDeposits[owner][tokenID].amount; } function getPendingNFTDepositAmount( address owner, address token, ExchangeData.NftType nftType, uint256 nftID ) public override view returns (uint96) { return state.pendingNFTDeposits[owner][nftType][token][nftID].amount; } // -- Withdrawals -- function forceWithdrawByTokenID( address owner, uint16 tokenID, uint32 accountID ) external override nonReentrant payable onlyFromUserOrAgent(owner) { state.forceWithdraw(owner, tokenID, accountID); } function forceWithdraw( address owner, address token, uint32 accountID ) external override nonReentrant payable onlyFromUserOrAgent(owner) { uint16 tokenID = state.getTokenID(token); state.forceWithdraw(owner, tokenID, accountID); } function isForcedWithdrawalPending( uint32 accountID, address token ) external override view returns (bool) { uint16 tokenID = state.getTokenID(token); return state.pendingForcedWithdrawals[accountID][tokenID].timestamp != 0; } function withdrawProtocolFees( address token ) external override nonReentrant payable { uint16 tokenID = state.getTokenID(token); state.forceWithdraw(address(0), tokenID, ExchangeData.ACCOUNTID_PROTOCOLFEE); } // We still alow anyone to withdraw these funds for the account owner function withdrawFromMerkleTree( ExchangeData.MerkleProof calldata merkleProof ) external override nonReentrant { state.withdrawFromMerkleTree(merkleProof); } function isWithdrawnInWithdrawalMode( uint32 accountID, address token ) external override view returns (bool) { uint16 tokenID = state.getTokenID(token); return state.withdrawnInWithdrawMode[accountID][tokenID]; } function withdrawFromDepositRequest( address owner, address token ) external override nonReentrant { state.withdrawFromDepositRequest( owner, token ); } function withdrawFromNFTDepositRequest( address owner, address token, ExchangeData.NftType nftType, uint256 nftID ) external override nonReentrant { state.withdrawFromNFTDepositRequest( owner, token, nftType, nftID ); } function withdrawFromApprovedWithdrawals( address[] calldata owners, address[] calldata tokens ) external override nonReentrant { state.withdrawFromApprovedWithdrawals( owners, tokens ); } function withdrawFromApprovedWithdrawalsNFT( address[] memory owners, address[] memory minters, ExchangeData.NftType[] memory nftTypes, address[] memory tokens, uint256[] memory nftIDs ) external override nonReentrant { state.withdrawFromApprovedWithdrawalsNFT( owners, minters, nftTypes, tokens, nftIDs ); } function getAmountWithdrawable( address owner, address token ) external override view returns (uint) { uint16 tokenID = state.getTokenID(token); return state.amountWithdrawable[owner][tokenID]; } function getAmountWithdrawableNFT( address owner, address token, ExchangeData.NftType nftType, uint256 nftID, address minter ) external override view returns (uint) { return state.amountWithdrawableNFT[owner][minter][nftType][token][nftID]; } function notifyForcedRequestTooOld( uint32 accountID, uint16 tokenID ) external override nonReentrant { ExchangeData.ForcedWithdrawal storage withdrawal = state.pendingForcedWithdrawals[accountID][tokenID]; require(withdrawal.timestamp != 0, "WITHDRAWAL_NOT_TOO_OLD"); // Check if the withdrawal has indeed exceeded the time limit require(block.timestamp >= withdrawal.timestamp + ExchangeData.MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE, "WITHDRAWAL_NOT_TOO_OLD"); // Enter withdrawal mode state.withdrawalModeStartTime = block.timestamp; emit WithdrawalModeActivated(state.withdrawalModeStartTime); } function setWithdrawalRecipient( address from, address to, address token, uint96 amount, uint32 storageID, address newRecipient ) external override nonReentrant onlyFromUserOrAgent(from) { require(newRecipient != address(0), "INVALID_DATA"); uint16 tokenID = state.getTokenID(token); require(state.withdrawalRecipient[from][to][tokenID][amount][storageID] == address(0), "CANNOT_OVERRIDE_RECIPIENT_ADDRESS"); state.withdrawalRecipient[from][to][tokenID][amount][storageID] = newRecipient; } function getWithdrawalRecipient( address from, address to, address token, uint96 amount, uint32 storageID ) external override view returns (address) { uint16 tokenID = state.getTokenID(token); return state.withdrawalRecipient[from][to][tokenID][amount][storageID]; } function onchainTransferFrom( address from, address to, address token, uint amount ) external override nonReentrant onlyFromUserOrAgent(from) { require(state.allowOnchainTransferFrom, "NOT_ALLOWED"); state.depositContract.transfer(from, to, token, amount); } function approveTransaction( address owner, bytes32 transactionHash ) external override nonReentrant onlyFromUserOrAgent(owner) { state.approvedTx[owner][transactionHash] = true; emit TransactionApproved(owner, transactionHash); } function approveTransactions( address[] calldata owners, bytes32[] calldata transactionHashes ) external override nonReentrant { require(owners.length == transactionHashes.length, "INVALID_DATA"); require(state.agentRegistry.isAgent(owners, msg.sender), "UNAUTHORIZED"); for (uint i = 0; i < owners.length; i++) { state.approvedTx[owners[i]][transactionHashes[i]] = true; } } function isTransactionApproved( address owner, bytes32 transactionHash ) external override view returns (bool) { return state.approvedTx[owner][transactionHash]; } function getDomainSeparator() external override view returns (bytes32) { return state.DOMAIN_SEPARATOR; } // -- Admins -- function setMaxAgeDepositUntilWithdrawable( uint32 newValue ) external override nonReentrant onlyOwner returns (uint32) { return state.setMaxAgeDepositUntilWithdrawable(newValue); } function getMaxAgeDepositUntilWithdrawable() external override view returns (uint32) { return state.maxAgeDepositUntilWithdrawable; } function shutdown() external override nonReentrant onlyOwner returns (bool success) { require(!state.isInWithdrawalMode(), "INVALID_MODE"); require(!state.isShutdown(), "ALREADY_SHUTDOWN"); state.shutdownModeStartTime = block.timestamp; emit Shutdown(state.shutdownModeStartTime); return true; } function getProtocolFeeValues() external override view returns ( uint32 syncedAt, uint8 takerFeeBips, uint8 makerFeeBips, uint8 previousTakerFeeBips, uint8 previousMakerFeeBips ) { syncedAt = state.protocolFeeData.syncedAt; takerFeeBips = state.protocolFeeData.takerFeeBips; makerFeeBips = state.protocolFeeData.makerFeeBips; previousTakerFeeBips = state.protocolFeeData.previousTakerFeeBips; previousMakerFeeBips = state.protocolFeeData.previousMakerFeeBips; } function setAmmFeeBips(uint8 _feeBips) external override nonReentrant onlyOwner { require(_feeBips <= 200, "INVALID_VALUE"); state.ammFeeBips = _feeBips; } function getAmmFeeBips() external override view returns (uint8) { return state.ammFeeBips; } function setAllowOnchainTransferFrom(bool value) external nonReentrant onlyOwner { require(state.allowOnchainTransferFrom != value, "SAME_VALUE"); state.allowOnchainTransferFrom = value; } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"blockIdx","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"publicDataHash","type":"bytes32"}],"name":"BlockSubmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"DepositRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"exchangeAddress","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"bytes32","name":"genesisMerkleRoot","type":"bytes32"}],"name":"ExchangeCloned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint16","name":"tokenID","type":"uint16"},{"indexed":false,"internalType":"uint32","name":"accountID","type":"uint32"}],"name":"ForcedWithdrawalRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint8","name":"nftType","type":"uint8"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftID","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"NFTDepositRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"category","type":"uint8"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint16","name":"tokenID","type":"uint16"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NftWithdrawalCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"category","type":"uint8"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint16","name":"tokenID","type":"uint16"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NftWithdrawalFailed","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":false,"internalType":"uint8","name":"takerFeeBips","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"makerFeeBips","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"previousTakerFeeBips","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"previousMakerFeeBips","type":"uint8"}],"name":"ProtocolFeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Shutdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint16","name":"tokenId","type":"uint16"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"bytes32","name":"transactionHash","type":"bytes32"}],"name":"TransactionApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"category","type":"uint8"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawalCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"category","type":"uint8"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawalFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"WithdrawalModeActivated","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"bytes32","name":"transactionHash","type":"bytes32"}],"name":"approveTransaction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"bytes32[]","name":"transactionHashes","type":"bytes32[]"}],"name":"approveTransactions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnExchangeStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"enum ExchangeData.NftType","name":"nftType","type":"uint8"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"nftID","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"depositNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"accountID","type":"uint32"}],"name":"forceWithdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint16","name":"tokenID","type":"uint16"},{"internalType":"uint32","name":"accountID","type":"uint32"}],"name":"forceWithdrawByTokenID","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getAgentRegistry","outputs":[{"internalType":"contract IAgentRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAmmFeeBips","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"getAmountWithdrawable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"enum ExchangeData.NftType","name":"nftType","type":"uint8"},{"internalType":"uint256","name":"nftID","type":"uint256"},{"internalType":"address","name":"minter","type":"address"}],"name":"getAmountWithdrawableNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlockHeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockIdx","type":"uint256"}],"name":"getBlockInfo","outputs":[{"components":[{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"bytes28","name":"blockDataHash","type":"bytes28"}],"internalType":"struct ExchangeData.BlockInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConstants","outputs":[{"components":[{"internalType":"uint256","name":"SNARK_SCALAR_FIELD","type":"uint256"},{"internalType":"uint256","name":"MAX_OPEN_FORCED_REQUESTS","type":"uint256"},{"internalType":"uint256","name":"MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE","type":"uint256"},{"internalType":"uint256","name":"TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS","type":"uint256"},{"internalType":"uint256","name":"MAX_NUM_ACCOUNTS","type":"uint256"},{"internalType":"uint256","name":"MAX_NUM_TOKENS","type":"uint256"},{"internalType":"uint256","name":"MIN_AGE_PROTOCOL_FEES_UNTIL_UPDATED","type":"uint256"},{"internalType":"uint256","name":"MIN_TIME_IN_SHUTDOWN","type":"uint256"},{"internalType":"uint256","name":"TX_DATA_AVAILABILITY_SIZE","type":"uint256"},{"internalType":"uint256","name":"MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE_UPPERBOUND","type":"uint256"}],"internalType":"struct ExchangeData.Constants","name":"","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getDepositContract","outputs":[{"internalType":"contract IDepositContract","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDomainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExchangeStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxAgeDepositUntilWithdrawable","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumAvailableForcedSlots","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getPendingDepositAmount","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"enum ExchangeData.NftType","name":"nftType","type":"uint8"},{"internalType":"uint256","name":"nftID","type":"uint256"}],"name":"getPendingNFTDepositAmount","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getProtocolFeeLastWithdrawnTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFeeValues","outputs":[{"internalType":"uint32","name":"syncedAt","type":"uint32"},{"internalType":"uint8","name":"takerFeeBips","type":"uint8"},{"internalType":"uint8","name":"makerFeeBips","type":"uint8"},{"internalType":"uint8","name":"previousTakerFeeBips","type":"uint8"},{"internalType":"uint8","name":"previousMakerFeeBips","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"tokenID","type":"uint16"}],"name":"getTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getTokenID","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint32","name":"storageID","type":"uint32"}],"name":"getWithdrawalRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_loopring","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"bytes32","name":"_genesisMerkleRoot","type":"bytes32"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"accountID","type":"uint32"},{"internalType":"address","name":"token","type":"address"}],"name":"isForcedWithdrawalPending","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isInWithdrawalMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isShutdown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"bytes32","name":"transactionHash","type":"bytes32"}],"name":"isTransactionApproved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"isUserOrAgent","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"accountID","type":"uint32"},{"internalType":"address","name":"token","type":"address"}],"name":"isWithdrawnInWithdrawalMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"accountID","type":"uint32"},{"internalType":"uint16","name":"tokenID","type":"uint16"}],"name":"notifyForcedRequestTooOld","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"onchainTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"refreshBlockVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"registerToken","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_agentRegistry","type":"address"}],"name":"setAgentRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"value","type":"bool"}],"name":"setAllowOnchainTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_feeBips","type":"uint8"}],"name":"setAmmFeeBips","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_depositContract","type":"address"}],"name":"setDepositContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newValue","type":"uint32"}],"name":"setMaxAgeDepositUntilWithdrawable","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint32","name":"storageID","type":"uint32"},{"internalType":"address","name":"newRecipient","type":"address"}],"name":"setWithdrawalRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shutdown","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"blockType","type":"uint8"},{"internalType":"uint16","name":"blockSize","type":"uint16"},{"internalType":"uint8","name":"blockVersion","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256[8]","name":"proof","type":"uint256[8]"},{"internalType":"bool","name":"storeBlockInfoOnchain","type":"bool"},{"internalType":"bytes","name":"auxiliaryData","type":"bytes"},{"internalType":"bytes","name":"offchainData","type":"bytes"}],"internalType":"struct ExchangeData.Block[]","name":"blocks","type":"tuple[]"}],"name":"submitBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawExchangeFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawExchangeStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"withdrawFromApprovedWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"address[]","name":"minters","type":"address[]"},{"internalType":"enum ExchangeData.NftType[]","name":"nftTypes","type":"uint8[]"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"nftIDs","type":"uint256[]"}],"name":"withdrawFromApprovedWithdrawalsNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"withdrawFromDepositRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint32","name":"accountID","type":"uint32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"pubKeyX","type":"uint256"},{"internalType":"uint256","name":"pubKeyY","type":"uint256"},{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"uint256","name":"feeBipsAMM","type":"uint256"}],"internalType":"struct ExchangeData.AccountLeaf","name":"accountLeaf","type":"tuple"},{"components":[{"internalType":"uint16","name":"tokenID","type":"uint16"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint256","name":"weightAMM","type":"uint256"},{"internalType":"uint256","name":"storageRoot","type":"uint256"}],"internalType":"struct ExchangeData.BalanceLeaf","name":"balanceLeaf","type":"tuple"},{"components":[{"internalType":"address","name":"minter","type":"address"},{"internalType":"enum ExchangeData.NftType","name":"nftType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nftID","type":"uint256"},{"internalType":"uint8","name":"creatorFeeBips","type":"uint8"}],"internalType":"struct ExchangeData.Nft","name":"nft","type":"tuple"},{"internalType":"uint256[48]","name":"accountMerkleProof","type":"uint256[48]"},{"internalType":"uint256[24]","name":"balanceMerkleProof","type":"uint256[24]"}],"internalType":"struct ExchangeData.MerkleProof","name":"merkleProof","type":"tuple"}],"name":"withdrawFromMerkleTree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"enum ExchangeData.NftType","name":"nftType","type":"uint8"},{"internalType":"uint256","name":"nftID","type":"uint256"}],"name":"withdrawFromNFTDepositRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdrawProtocolFees","outputs":[],"stateMutability":"payable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50600080546001600160a01b03191633179055615cc780620000336000396000f3fe6080604052600436106103c35760003560e01c806391b11ad4116101f2578063cdb1b44b1161010d578063ed24911d116100a0578063f698da251161006f578063f698da2514610a77578063f732e02114610aec578063fc0e74d114610b01578063fcd7810c14610b16576103c3565b8063ed24911d14610a77578063ef36521814610a8c578063f23a6e6114610aac578063f2fde38b14610acc576103c3565b8063d59acd25116100dc578063d59acd2514610a02578063d5b039ce14610a22578063de6ff7cd14610a42578063e30c397814610a62576103c3565b8063cdb1b44b1461098f578063cdb4e8c3146109af578063ce2ec5de146109c2578063cf4bc4c1146109e2576103c3565b8063ab94276a11610185578063bf86d69011610154578063bf86d69014610925578063c8e26cae1461093a578063c97890f11461095a578063cd097b4f1461096f576103c3565b8063ab94276a146108b0578063b1a417f4146108c5578063bb141cf4146108d8578063bc197c8114610905576103c3565b8063960af2d9116101c1578063960af2d914610839578063972f7565146108595780639a295e731461086e578063a75f8a4e14610890576103c3565b806391b11ad4146107cf57806391cae372146107e457806392f54afc146107f957806393b8098714610819576103c3565b806349590657116102e257806369b91432116102755780637d54f248116102445780637d54f2481461075a5780638a554abe1461077a5780638d2a88881461079a5780638da5cb5b146107ba576103c3565b806369b91432146106fb578063715018a6146107105780637bb96acb146107255780637d3636011461073a576103c3565b806354fd4d50116102b157806354fd4d50146106795780636008cd1f1461069b5780636133f985146106bb57806363f8071c146106db576103c3565b8063495906571461060d5780634a14cd84146106225780634e71e0c8146106445780635322843014610659576103c3565b806328342ecf1161035a578063327e965e11610329578063327e965e14610587578063438c2a42146105a75780634597d3ce146105d457806347b67d05146105fa576103c3565b806328342ecf146105075780632c3d356b146105275780632d80caa5146105545780632fa5825f14610567576103c3565b80630ec2e821116103965780630ec2e8211461046d578063150b7a021461048d5780631ef36835146104ba57806328068da3146104e7576103c3565b806301b1eb07146103c85780630394bc2b146103ea57806305987d571461040a57806309824a8014610440575b600080fd5b3480156103d457600080fd5b506103e86103e3366004614af3565b610b38565b005b3480156103f657600080fd5b506103e8610405366004614781565b610c7e565b34801561041657600080fd5b5061042a6104253660046147bf565b610d51565b60405161043791906150ce565b60405180910390f35b34801561044c57600080fd5b5061046061045b3660046143b4565b610de4565b6040516104379190615ada565b34801561047957600080fd5b506103e86104883660046143b4565b610f26565b34801561049957600080fd5b506104ad6104a8366004614816565b61109f565b6040516104379190615110565b3480156104c657600080fd5b506104da6104d5366004614500565b6110c8565b6040516104379190614efc565b3480156104f357600080fd5b506103e8610502366004614424565b61114a565b34801561051357600080fd5b506103e86105223660046143b4565b6112b8565b34801561053357600080fd5b50610547610542366004614be3565b611431565b60405161043791906150c3565b6103e86105623660046143b4565b611471565b34801561057357600080fd5b5061042a6105823660046143ec565b61154f565b34801561059357600080fd5b506103e86105a23660046149f4565b61159b565b3480156105b357600080fd5b506105c76105c23660046143ec565b611661565b6040516104379190615b38565b3480156105e057600080fd5b506105e96116bb565b604051610437959493929190615afa565b6103e86106083660046148e7565b6116fe565b34801561061957600080fd5b5061042a6117e2565b34801561062e57600080fd5b506106376117e8565b6040516104379190615ae9565b34801561065057600080fd5b506103e86117f4565b34801561066557600080fd5b506103e8610674366004614ab3565b6118db565b34801561068557600080fd5b5061068e6119c0565b604051610437919061513d565b3480156106a757600080fd5b506105476106b6366004614931565b6119f7565b3480156106c757600080fd5b506103e86106d636600461469b565b611a2f565b3480156106e757600080fd5b506104606106f63660046143b4565b611c42565b34801561070757600080fd5b5061042a611c57565b34801561071c57600080fd5b506103e8611c68565b34801561073157600080fd5b5061042a611d28565b34801561074657600080fd5b506105c7610755366004614781565b611d2e565b34801561076657600080fd5b506103e86107753660046146db565b611dbf565b34801561078657600080fd5b506105476107953660046143b4565b611edf565b3480156107a657600080fd5b506103e86107b53660046143ec565b611fca565b3480156107c657600080fd5b506104da61205e565b3480156107db57600080fd5b5061054761207a565b3480156107f057600080fd5b506104da612086565b34801561080557600080fd5b50610547610814366004614be3565b6120a2565b34801561082557600080fd5b506103e8610834366004614b2b565b612103565b34801561084557600080fd5b50610637610854366004614bab565b6121cd565b34801561086557600080fd5b506103e8612304565b34801561087a57600080fd5b506108836124ac565b6040516104379190615593565b34801561089c57600080fd5b506103e86108ab3660046143ec565b61253f565b3480156108bc57600080fd5b506104da612789565b6103e86108d3366004614474565b6127a5565b3480156108e457600080fd5b506108f86108f3366004614b7b565b61286f565b6040516104379190615554565b34801561091157600080fd5b506104ad6109203660046145f1565b6128cf565b34801561093157600080fd5b506105476128f9565b34801561094657600080fd5b5061042a6109553660046143b4565b612905565b34801561096657600080fd5b506103e861292d565b34801561097b57600080fd5b506103e861098a36600461498b565b612b9d565b34801561099b57600080fd5b506103e86109aa36600461498b565b612c35565b6103e86109bd36600461495c565b612e55565b3480156109ce57600080fd5b5061042a6109dd3660046143b4565b612f29565b3480156109ee57600080fd5b506103e86109fd366004614c2d565b613060565b348015610a0e57600080fd5b506103e8610a1d366004614931565b61317f565b348015610a2e57600080fd5b506103e8610a3d366004614570565b61329b565b348015610a4e57600080fd5b506103e8610a5d366004614c00565b6134b6565b348015610a6e57600080fd5b506104da61360a565b348015610a8357600080fd5b5061042a613626565b348015610a9857600080fd5b506104da610aa7366004614b43565b61362c565b348015610ab857600080fd5b506104ad610ac7366004614880565b6136d2565b348015610ad857600080fd5b506103e8610ae73660046143b4565b6136fc565b348015610af857600080fd5b5061042a61380a565b348015610b0d57600080fd5b506105476138b1565b348015610b2257600080fd5b50610b2b613a0e565b6040516104379190615b2a565b60025415610b7b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60405180910390fd5b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314610bd1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b60195460ff75010000000000000000000000000000000000000000009091041615158115151415610c2e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061529a565b601980549115157501000000000000000000000000000000000000000000027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff9092169190911790556000600255565b60025415610cb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517feaacf5e40000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899063eaacf5e490610d1690600390889088908890889060040161570f565b60006040518083038186803b158015610d2e57600080fd5b505af4158015610d42573d6000803e3d6000fd5b50506000600255505050505050565b73ffffffffffffffffffffffffffffffffffffffff8086166000908152601b60209081526040808320938516835292905290812081856001811115610d9257fe5b6001811115610d9d57fe5b81526020808201929092526040908101600090812073ffffffffffffffffffffffffffffffffffffffff891682528352818120868252909252902054905095945050505050565b6000600254600014610e22576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314610e78576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517f36d626e90000000000000000000000000000000000000000000000000000000081527324f51fadc255dfcbc436615167c325cac7010b29906336d626e990610ecb906003908690600401615604565b60206040518083038186803b158015610ee357600080fd5b505af4158015610ef7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b9190614b5f565b600060025592915050565b60025415610f60576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314610fb6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff8116611003576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b60085473ffffffffffffffffffffffffffffffffffffffff1615611053576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061522c565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790556000600255565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6000806110d6600386613a2f565b73ffffffffffffffffffffffffffffffffffffffff80891660009081526013602090815260408083208b85168452825280832061ffff90951683529381528382206bffffffffffffffffffffffff89168352815283822063ffffffff88168352905291909120541691505095945050505050565b60025415611184576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002558361119381611edf565b6111c9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6019547501000000000000000000000000000000000000000000900460ff1661121e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615308565b6008546040517ff18d03cc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063f18d03cc9061127a908890889088908890600401614f95565b600060405180830381600087803b15801561129457600080fd5b505af11580156112a8573d6000803e3d6000fd5b5050600060025550505050505050565b600254156112f2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314611348576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff8116611395576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b60075473ffffffffffffffffffffffffffffffffffffffff16156113e5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061522c565b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790556000600255565b60008061143f600384613a2f565b63ffffffff85166000908152600e6020908152604080832061ffff9094168352929052205460ff169150505b92915050565b600254156114ab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560006114bd600383613a2f565b6040517ebaadff0000000000000000000000000000000000000000000000000000000081529091507346a71e786c34714928390782e9e50ee1c2fa99899062baadff906115169060039060009086908290600401615628565b60006040518083038186803b15801561152e57600080fd5b505af4158015611542573d6000803e3d6000fd5b5050600060025550505050565b60008061155d600384613a2f565b73ffffffffffffffffffffffffffffffffffffffff85166000908152600f6020908152604080832061ffff9094168352929052205491505092915050565b600254156115d5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517fef22a02f0000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899063ef22a02f9061163590600390899089908990899089906004016157c1565b60006040518083038186803b15801561164d57600080fd5b505af41580156112a8573d6000803e3d6000fd5b60008061166f600384613a2f565b73ffffffffffffffffffffffffffffffffffffffff8516600090815260116020908152604080832061ffff909416835292905220546bffffffffffffffffffffffff1691505092915050565b60155463ffffffff81169160ff64010000000083048116926501000000000081048216926601000000000000820483169267010000000000000090920490911690565b60025415611738576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002558261174781611edf565b61177d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b600061178a600385613a2f565b6040517ebaadff0000000000000000000000000000000000000000000000000000000081529091507346a71e786c34714928390782e9e50ee1c2fa99899062baadff9061163590600390899086908990600401615628565b60095490565b60035463ffffffff1690565b60015473ffffffffffffffffffffffffffffffffffffffff163314611845576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6001546000805460405173ffffffffffffffffffffffffffffffffffffffff93841693909116917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a360018054600080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff841617909155169055565b60025415611915576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff16331461196b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517f5ee5495b0000000000000000000000000000000000000000000000000000000081527394cdf6621c190f5950ed9e16debb19e6a8d2e84e90635ee5495b90611516906003908690869060040161585f565b60408051808201909152600581527f332e362e30000000000000000000000000000000000000000000000000000000602082015290565b73ffffffffffffffffffffffffffffffffffffffff919091166000908152601260209081526040808320938352929052205460ff1690565b60025415611a69576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560195473ffffffffffffffffffffffffffffffffffffffff16158015611a945750600954155b611aca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615150565b73ffffffffffffffffffffffffffffffffffffffff8216611b17576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b6000805473ffffffffffffffffffffffffffffffffffffffff8085167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790925560198054928616929091169190911790556040805160a081019091526011606082019081527f4c6f6f7072696e672050726f746f636f6c00000000000000000000000000000060808301528152730e3229004b55ed8edcc29473c790f33c2b693d3890633aa277299060039086908590611be99060208101611bdb6119c0565b815230602090910152613aba565b6040518563ffffffff1660e01b8152600401611c089493929190615757565b60006040518083038186803b158015611c2057600080fd5b505af4158015611c34573d6000803e3d6000fd5b505060006002555050505050565b6000611c4f600383613a2f565b90505b919050565b6000611c636003613b31565b905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314611cb9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6000805460405173ffffffffffffffffffffffffffffffffffffffff909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600b5490565b73ffffffffffffffffffffffffffffffffffffffff84166000908152601a6020526040812081846001811115611d6057fe5b6001811115611d6b57fe5b81526020808201929092526040908101600090812073ffffffffffffffffffffffffffffffffffffffff8816825283528181208582529092529020546bffffffffffffffffffffffff169050949350505050565b60025415611df9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025587611e0881611edf565b611e3e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517f74417afc0000000000000000000000000000000000000000000000000000000081527307f50b5a6a3f1f069c27ea11ffa62b53a20872cc906374417afc90611e9f906003908d908d908d908d908d908d908d908d9060040161568f565b60006040518083038186803b158015611eb757600080fd5b505af4158015611ecb573d6000803e3d6000fd5b505060006002555050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8216331480611c4f575060075473ffffffffffffffffffffffffffffffffffffffff1615801590611c4f57506007546040517f4ebf59d200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690634ebf59d290611f7a9085903390600401614f1d565b60206040518083038186803b158015611f9257600080fd5b505afa158015611fa6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4f9190614b0f565b60025415612004576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517f95d755550000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa9989906395d75555906115169060039086908690600401615663565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b6000611c636003613b42565b60075473ffffffffffffffffffffffffffffffffffffffff1690565b6000806120b0600384613a2f565b63ffffffff8516600090815260106020908152604080832061ffff9094168352929052205467ffffffffffffffff7401000000000000000000000000000000000000000090910416151591505092915050565b6002541561213d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517fcae3c7810000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899063cae3c781906121959060039085906004016159eb565b60006040518083038186803b1580156121ad57600080fd5b505af41580156121c1573d6000803e3d6000fd5b50506000600255505050565b600060025460001461220b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314612261576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517f1854b85c00000000000000000000000000000000000000000000000000000000815273b9c5d84f8fc65aa921d306e37f2dba9b19ec389190631854b85c906122b4906003908690600401615ac6565b60206040518083038186803b1580156122cc57600080fd5b505af41580156122e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b9190614bc7565b6002541561233e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025561234d6003613b42565b156124a5576005546040517f883515c100000000000000000000000000000000000000000000000000000000815260009173ffffffffffffffffffffffffffffffffffffffff169063883515c1906123a9903090600401614efc565b60206040518083038186803b1580156123c157600080fd5b505afa1580156123d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f99190614b93565b6005546040517fc51c820800000000000000000000000000000000000000000000000000000000815291925073ffffffffffffffffffffffffffffffffffffffff169063c51c8208906124509084906004016150ce565b602060405180830381600087803b15801561246a57600080fd5b505af115801561247e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a29190614b93565b50505b6000600255565b6124b46140cd565b6040518061014001604052807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001815260200161100081526020016213c680815260200162093a808152602001640100000000815260200162010000815260200162093a80815260200162278d008152602001604481526020016213c68063ffffffff16815250905090565b60025415612579576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff1633146125cf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff811661261c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615187565b60085473ffffffffffffffffffffffffffffffffffffffff83811691161415612671576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615263565b73ffffffffffffffffffffffffffffffffffffffff82166126b757476126b0815a73ffffffffffffffffffffffffffffffffffffffff85169190613b4b565b50506124a2565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8416906370a082319061270c903090600401614efc565b60206040518083038186803b15801561272457600080fd5b505afa158015612738573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275c9190614b93565b905061277f73ffffffffffffffffffffffffffffffffffffffff84168383613bae565b5050506000600255565b60085473ffffffffffffffffffffffffffffffffffffffff1690565b600254156127df576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b6001600255856127ee81611edf565b612824576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b611c348787878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060039796959493925050613bbf9050565b612877614120565b506000908152600a602090815260409182902082518084019093525463ffffffff811683526401000000009004811b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000169082015290565b7fbc197c810000000000000000000000000000000000000000000000000000000095945050505050565b6000611c636003613e49565b73ffffffffffffffffffffffffffffffffffffffff1660009081526018602052604090205490565b60025415612967576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff1633146129bd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b600554604080517f8d80b252000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff1691638d80b252916004808301926020929190829003018186803b158015612a2857600080fd5b505afa158015612a3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a6091906143d0565b73ffffffffffffffffffffffffffffffffffffffff161415612aae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b600554604080517f8d80b252000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff90921691638d80b25291600480820192602092909190829003018186803b158015612b1957600080fd5b505afa158015612b2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b5191906143d0565b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790556000600255565b60025415612bd7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517ff460cca90000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899063f460cca990610d16906003908890889088908890600401615788565b60025415612c6f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b6001600255828114612cad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061551d565b6007546040517f2eac95f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690632eac95f190612d0790879087903390600401615049565b60206040518083038186803b158015612d1f57600080fd5b505afa158015612d33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d579190614b0f565b612d8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b60005b838110156121c157600160126000878785818110612daa57fe5b9050602002016020810190612dbf91906143b4565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000858585818110612e0757fe5b6020908102929092013583525081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055600101612d90565b60025415612e8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025582612e9e81611edf565b612ed4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517ebaadff0000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899062baadff90610d1690600390889088908890600401615628565b6000600254600014612f67576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314612fbd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517ff93234a200000000000000000000000000000000000000000000000000000000815273b9c5d84f8fc65aa921d306e37f2dba9b19ec38919063f93234a290613010906003908690600401615604565b60206040518083038186803b15801561302857600080fd5b505af415801561303c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b9190614b93565b6002541561309a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff1633146130f0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b60c88160ff16111561312e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906154e6565b6019805460ff90921674010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff9092169190911790556000600255565b600254156131b9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b6001600255816131c881611edf565b6131fe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff831660009081526012602090815260408083208584529091529081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f7115b98931e7aca6d0aa11d10fe28877316a661a44c4bfc93c76b19dbbf5b107906132899085908590615023565b60405180910390a15050600060025550565b600254156132d5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b6001600255856132e481611edf565b61331a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff8216613367576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061551d565b6000613374600387613a2f565b73ffffffffffffffffffffffffffffffffffffffff89811660009081526013602090815260408083208c85168452825280832061ffff8616845282528083206bffffffffffffffffffffffff8b168452825280832063ffffffff8a1684529091529020549192501615613413576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615489565b73ffffffffffffffffffffffffffffffffffffffff9788166000908152601360209081526040808320998b16835298815288822061ffff90931682529182528781206bffffffffffffffffffffffff909616815294815286852063ffffffff9094168552929092529382208054959094167fffffffffffffffffffffffff0000000000000000000000000000000000000000909516949094179092555060025550565b600254156134f0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025563ffffffff8216600090815260106020908152604080832061ffff851684529091529020805474010000000000000000000000000000000000000000900467ffffffffffffffff16613573576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615452565b805467ffffffffffffffff74010000000000000000000000000000000000000000909104166213c680014210156135d6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615452565b4260178190556040517f5b8f5ce93a49fc6eab534327f9c77fd2966e16278d6135cc0d99e6b6450c796391613289916150ce565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b60045490565b6040517ff24789a80000000000000000000000000000000000000000000000000000000081526000907324f51fadc255dfcbc436615167c325cac7010b299063f24789a890613682906003908690600401615ab4565b60206040518083038186803b15801561369a57600080fd5b505af41580156136ae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4f91906143d0565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461374d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff81161580159061378d575060005473ffffffffffffffffffffffffffffffffffffffff828116911614155b6137c3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615187565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6005546040517f883515c100000000000000000000000000000000000000000000000000000000815260009173ffffffffffffffffffffffffffffffffffffffff169063883515c190613861903090600401614efc565b60206040518083038186803b15801561387957600080fd5b505afa15801561388d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c639190614b93565b60006002546000146138ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314613945576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b61394f6003613b42565b15613986576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615376565b6139906003613e49565b156139c7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906153ad565b4260168190556040517f574214b195bf5273a95bb4498e35cf1fde0ce327c727a95ec2ab359f7ba4e11a916139fb916150ce565b60405180910390a1506001600060025590565b60195474010000000000000000000000000000000000000000900460ff1690565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600a8301602052604090205461ffff1680613a92576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906153e4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f836000015180519060200120846020015180519060200120838660400151604051602001613b139594939291906150d7565b60405160208183030381529060405280519060200120915050919050565b6011015463ffffffff166110000390565b60140154151590565b6000613b6e73ffffffffffffffffffffffffffffffffffffffff85168484613e52565b905080613ba7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906152d1565b9392505050565b613bba8383835a613ef9565b505050565b73ffffffffffffffffffffffffffffffffffffffff8416613c0c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b6000613c188785613a2f565b60058801546040517ff3d790b700000000000000000000000000000000000000000000000000000000815291925060009173ffffffffffffffffffffffffffffffffffffffff9091169063f3d790b7903490613c7e908b908a908a908a90600401614fcc565b6020604051808303818588803b158015613c9757600080fd5b505af1158015613cab573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613cd09190614c47565b73ffffffffffffffffffffffffffffffffffffffff87166000908152600e8a016020908152604080832061ffff871684528252918290208251808401909352546bffffffffffffffffffffffff168083524267ffffffffffffffff169183019190915291925090613d419083613f41565b6bffffffffffffffffffffffff908116825273ffffffffffffffffffffffffffffffffffffffff88166000908152600e8b016020908152604080832061ffff8816845282529182902084518154928601517fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009093169416939093177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff166c0100000000000000000000000067ffffffffffffffff9092169190910217909155517f73ff7b101bcdc22f199e8e1dd9893170a683d6897be4f1086ca05705abb886ae90613e36908a908a908a9088908890614f44565b60405180910390a1505050505050505050565b60130154151590565b600082613e6157506001613ba7565b6000613e828573ffffffffffffffffffffffffffffffffffffffff16613f8f565b90508073ffffffffffffffffffffffffffffffffffffffff16848490604051613eaa90613f8f565b600060405180830381858888f193505050503d8060008114613ee8576040519150601f19603f3d011682016040523d82523d6000602084013e613eed565b606091505b50909695505050505050565b613f0584848484613f92565b613f3b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906152d1565b50505050565b8181016bffffffffffffffffffffffff808416908216101561146b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061541b565b90565b60008063a9059cbb60e01b8585604051602401613fb0929190615023565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008673ffffffffffffffffffffffffffffffffffffffff1684836040516140379190614ee0565b60006040518083038160008787f1925050503d8060008114614075576040519150601f19603f3d011682016040523d82523d6000602084013e61407a565b606091505b5050905061408781614092565b979650505050505050565b600081156140c9573d80156140b257602081146140bb57600092506140c7565b600192506140c7565b60206000803e60005192505b505b5090565b604051806101400160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604080518082019091526000808252602082015290565b60008083601f840112614148578182fd5b50813567ffffffffffffffff81111561415f578182fd5b602083019150836020808302850101111561417957600080fd5b9250929050565b600082601f830112614190578081fd5b813560206141a56141a083615b75565b615b51565b82815281810190858301838502870184018810156141c1578586fd5b855b858110156141e85781356141d681615c22565b845292840192908401906001016141c3565b5090979650505050505050565b600082601f830112614205578081fd5b813560206142156141a083615b75565b8281528181019085830183850287018401881015614231578586fd5b855b858110156141e8576142448261437e565b84529284019290840190600101614233565b600082601f830112614266578081fd5b813560206142766141a083615b75565b8281528181019085830183850287018401881015614292578586fd5b855b858110156141e857813584529284019290840190600101614294565b8035611c5281615c47565b60008083601f8401126142cc578182fd5b50813567ffffffffffffffff8111156142e3578182fd5b60208301915083602082850101111561417957600080fd5b600082601f83011261430b578081fd5b813567ffffffffffffffff81111561431f57fe5b61435060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601615b51565b818152846020838601011115614364578283fd5b816020850160208301379081016020019190915292915050565b803560028110611c5257600080fd5b8035611c5281615c55565b8035611c5281615c65565b803560ff81168114611c5257600080fd5b6000602082840312156143c5578081fd5b8135613ba781615c22565b6000602082840312156143e1578081fd5b8151613ba781615c22565b600080604083850312156143fe578081fd5b823561440981615c22565b9150602083013561441981615c22565b809150509250929050565b60008060008060808587031215614439578182fd5b843561444481615c22565b9350602085013561445481615c22565b9250604085013561446481615c22565b9396929550929360600135925050565b60008060008060008060a0878903121561448c578384fd5b863561449781615c22565b955060208701356144a781615c22565b945060408701356144b781615c22565b935060608701356144c781615c77565b9250608087013567ffffffffffffffff8111156144e2578283fd5b6144ee89828a016142bb565b979a9699509497509295939492505050565b600080600080600060a08688031215614517578283fd5b853561452281615c22565b9450602086013561453281615c22565b9350604086013561454281615c22565b9250606086013561455281615c77565b9150608086013561456281615c65565b809150509295509295909350565b60008060008060008060c08789031215614588578384fd5b863561459381615c22565b955060208701356145a381615c22565b945060408701356145b381615c22565b935060608701356145c381615c77565b925060808701356145d381615c65565b915060a08701356145e381615c22565b809150509295509295509295565b600080600080600060a08688031215614608578283fd5b853561461381615c22565b9450602086013561462381615c22565b9350604086013567ffffffffffffffff8082111561463f578485fd5b61464b89838a01614256565b94506060880135915080821115614660578283fd5b61466c89838a01614256565b93506080880135915080821115614681578283fd5b5061468e888289016142fb565b9150509295509295909350565b6000806000606084860312156146af578081fd5b83356146ba81615c22565b925060208401356146ca81615c22565b929592945050506040919091013590565b60008060008060008060008060e0898b0312156146f6578586fd5b883561470181615c22565b9750602089013561471181615c22565b965061471f60408a0161437e565b9550606089013561472f81615c22565b94506080890135935060a089013561474681615c77565b925060c089013567ffffffffffffffff811115614761578283fd5b61476d8b828c016142bb565b999c989b5096995094979396929594505050565b60008060008060808587031215614796578182fd5b84356147a181615c22565b935060208501356147b181615c22565b92506144646040860161437e565b600080600080600060a086880312156147d6578283fd5b85356147e181615c22565b945060208601356147f181615c22565b93506147ff6040870161437e565b925060608601359150608086013561456281615c22565b6000806000806080858703121561482b578182fd5b843561483681615c22565b9350602085013561484681615c22565b925060408501359150606085013567ffffffffffffffff811115614868578182fd5b614874878288016142fb565b91505092959194509250565b600080600080600060a08688031215614897578283fd5b85356148a281615c22565b945060208601356148b281615c22565b93506040860135925060608601359150608086013567ffffffffffffffff8111156148db578182fd5b61468e888289016142fb565b6000806000606084860312156148fb578081fd5b833561490681615c22565b9250602084013561491681615c22565b9150604084013561492681615c65565b809150509250925092565b60008060408385031215614943578182fd5b823561494e81615c22565b946020939093013593505050565b600080600060608486031215614970578081fd5b833561497b81615c22565b9250602084013561491681615c55565b600080600080604085870312156149a0578182fd5b843567ffffffffffffffff808211156149b7578384fd5b6149c388838901614137565b909650945060208701359150808211156149db578384fd5b506149e887828801614137565b95989497509550505050565b600080600080600060a08688031215614a0b578283fd5b853567ffffffffffffffff80821115614a22578485fd5b614a2e89838a01614180565b96506020880135915080821115614a43578485fd5b614a4f89838a01614180565b95506040880135915080821115614a64578485fd5b614a7089838a016141f5565b94506060880135915080821115614a85578283fd5b614a9189838a01614180565b93506080880135915080821115614aa6578283fd5b5061468e88828901614256565b60008060208385031215614ac5578182fd5b823567ffffffffffffffff811115614adb578283fd5b614ae785828601614137565b90969095509350505050565b600060208284031215614b04578081fd5b8135613ba781615c47565b600060208284031215614b20578081fd5b8151613ba781615c47565b6000610ae08284031215614b3d578081fd5b50919050565b600060208284031215614b54578081fd5b8135613ba781615c55565b600060208284031215614b70578081fd5b8151613ba781615c55565b600060208284031215614b8c578081fd5b5035919050565b600060208284031215614ba4578081fd5b5051919050565b600060208284031215614bbc578081fd5b8135613ba781615c65565b600060208284031215614bd8578081fd5b8151613ba781615c65565b60008060408385031215614bf5578182fd5b823561440981615c65565b60008060408385031215614c12578182fd5b8235614c1d81615c65565b9150602083013561441981615c55565b600060208284031215614c3e578081fd5b613ba7826143a3565b600060208284031215614c58578081fd5b8151613ba781615c77565b60008284526020808501945082825b85811015614cad578135614c8581615c22565b73ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614c72565b509495945050505050565b6000815180845260208085019450808401835b83811015614cad57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614ccb565b61030080828437600081840152505050565b61060080828437600081840152505050565b61010080828437600081840152505050565b6000815180845260208085019450808401835b83811015614cad57815187529582019590820190600101614d46565b15159052565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b60008151808452614dc8816020860160208601615bf6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60028110614e0457fe5b9052565b8035614e1381615c55565b61ffff1682526020810135614e2781615c77565b6bffffffffffffffffffffffff16602083015260408181013590830152606090810135910152565b8035614e5a81615c22565b73ffffffffffffffffffffffffffffffffffffffff8082168452614e806020840161437e565b9150614e8f6020850183614dfa565b60408301359150614e9f82615c22565b1660408301526060818101359083015260ff614ebd608083016143a3565b1660808301525050565b61ffff169052565b63ffffffff169052565b60ff169052565b60008251614ef2818460208701615bf6565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9586168152938516602085015291909316604083015261ffff90921660608201526bffffffffffffffffffffffff909116608082015260a00190565b73ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152506bffffffffffffffffffffffff84166040830152608060608301526150196080830184614db0565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b6040808252810183905260008460608301825b8681101561509957823561506f81615c22565b73ffffffffffffffffffffffffffffffffffffffff1682526020928301929091019060010161505c565b50809250505073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b901515815260200190565b90815260200190565b94855260208501939093526040840191909152606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00190565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b600060208252613ba76020830184614db0565b6020808252600b908201527f494e495449414c495a4544000000000000000000000000000000000000000000604082015260600190565b6020808252600f908201527f494e56414c49445f414444524553530000000000000000000000000000000000604082015260600190565b6020808252600a908201527f5245454e5452414e435900000000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f554e415554484f52495a45440000000000000000000000000000000000000000604082015260600190565b6020808252600b908201527f414c52454144595f534554000000000000000000000000000000000000000000604082015260600190565b6020808252600d908201527f494e56414c49445f544f4b454e00000000000000000000000000000000000000604082015260600190565b6020808252600a908201527f53414d455f56414c554500000000000000000000000000000000000000000000604082015260600190565b60208082526010908201527f5452414e534645525f4641494c55524500000000000000000000000000000000604082015260600190565b6020808252600b908201527f4e4f545f414c4c4f574544000000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f5a45524f5f414444524553530000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f494e56414c49445f4d4f44450000000000000000000000000000000000000000604082015260600190565b60208082526010908201527f414c52454144595f53485554444f574e00000000000000000000000000000000604082015260600190565b6020808252600f908201527f544f4b454e5f4e4f545f464f554e440000000000000000000000000000000000604082015260600190565b6020808252600c908201527f4144445f4f564552464c4f570000000000000000000000000000000000000000604082015260600190565b60208082526016908201527f5749544844524157414c5f4e4f545f544f4f5f4f4c4400000000000000000000604082015260600190565b60208082526021908201527f43414e4e4f545f4f564552524944455f524543495049454e545f41444452455360408201527f5300000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252600d908201527f494e56414c49445f56414c554500000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f494e56414c49445f444154410000000000000000000000000000000000000000604082015260600190565b815163ffffffff1681526020918201517fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000169181019190915260400190565b600061014082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525092915050565b91825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b93845273ffffffffffffffffffffffffffffffffffffffff92909216602084015261ffff16604083015263ffffffff16606082015260800190565b92835273ffffffffffffffffffffffffffffffffffffffff918216602084015216604082015260600190565b60006101008b835273ffffffffffffffffffffffffffffffffffffffff808c166020850152808b1660408501526156c9606085018b614dfa565b8089166080850152508660a08401526bffffffffffffffffffffffff861660c08401528060e08401526156ff8184018587614d68565b9c9b505050505050505050505050565b85815273ffffffffffffffffffffffffffffffffffffffff85811660208301528416604082015260a081016157476060830185614dfa565b8260808301529695505050505050565b93845273ffffffffffffffffffffffffffffffffffffffff9290921660208401526040830152606082015260800190565b6000868252606060208301526157a2606083018688614c63565b82810360408401526157b5818587614c63565b98975050505050505050565b6000878252602060c0818401526157db60c0840189614cb8565b83810360408501526157ed8189614cb8565b84810360608601528751808252838901925090830190845b8181101561582857615818838551614dfa565b9284019291840191600101615805565b5050848103608086015261583c8188614cb8565b9250505082810360a08401526158528185614d33565b9998505050505050505050565b83815260406020808301829052828201849052600091906060908185018682028601830188865b898110156159db577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa089840301845281357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe218c36030181126158e6578889fd5b8b016101e06158fd856158f8846143a3565b614ed9565b61590887830161438d565b61591488870182614ec7565b506159208983016143a3565b61592c8a870182614ed9565b5061593988830183615b93565b828a88015261594b8388018284614d68565b92505050608061595f818701828501614d21565b5061018061596e8184016142b0565b61597a82880182614d62565b50506101a061598b81840184615b93565b8784038389015261599d848284614d68565b93505050506101c06159b181840184615b93565b9350868303828801526159c5838583614d68565b9789019796505050928601925050600101615886565b50909a9950505050505050505050565b828152610b00810182356159fe81615c65565b63ffffffff81166020840152506020830135615a1981615c22565b73ffffffffffffffffffffffffffffffffffffffff81166040840152506040830135606083015260608301356080830152615a5660808401614398565b615a6360a0840182614ecf565b5060a083013560c0830152615a7e60e0830160c08501614e08565b615a9061016083016101408501614e4f565b615aa261020083016101e08501614d0f565b613ba761080083016107e08501614cfd565b91825261ffff16602082015260400190565b91825263ffffffff16602082015260400190565b61ffff91909116815260200190565b63ffffffff91909116815260200190565b63ffffffff95909516855260ff938416602086015291831660408501528216606084015216608082015260a00190565b60ff91909116815260200190565b6bffffffffffffffffffffffff91909116815260200190565b60405181810167ffffffffffffffff81118282101715615b6d57fe5b604052919050565b600067ffffffffffffffff821115615b8957fe5b5060209081020190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615bc7578283fd5b830160208101925035905067ffffffffffffffff811115615be757600080fd5b80360383131561417957600080fd5b60005b83811015615c11578181015183820152602001615bf9565b83811115613f3b5750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114615c4457600080fd5b50565b8015158114615c4457600080fd5b61ffff81168114615c4457600080fd5b63ffffffff81168114615c4457600080fd5b6bffffffffffffffffffffffff81168114615c4457600080fdfea264697066735822122090e1d0b4b5978ee2e8ccae3378a2c50a98e4f9a781297a63e9886657ead8f53664736f6c63430007060033
Deployed Bytecode
0x6080604052600436106103c35760003560e01c806391b11ad4116101f2578063cdb1b44b1161010d578063ed24911d116100a0578063f698da251161006f578063f698da2514610a77578063f732e02114610aec578063fc0e74d114610b01578063fcd7810c14610b16576103c3565b8063ed24911d14610a77578063ef36521814610a8c578063f23a6e6114610aac578063f2fde38b14610acc576103c3565b8063d59acd25116100dc578063d59acd2514610a02578063d5b039ce14610a22578063de6ff7cd14610a42578063e30c397814610a62576103c3565b8063cdb1b44b1461098f578063cdb4e8c3146109af578063ce2ec5de146109c2578063cf4bc4c1146109e2576103c3565b8063ab94276a11610185578063bf86d69011610154578063bf86d69014610925578063c8e26cae1461093a578063c97890f11461095a578063cd097b4f1461096f576103c3565b8063ab94276a146108b0578063b1a417f4146108c5578063bb141cf4146108d8578063bc197c8114610905576103c3565b8063960af2d9116101c1578063960af2d914610839578063972f7565146108595780639a295e731461086e578063a75f8a4e14610890576103c3565b806391b11ad4146107cf57806391cae372146107e457806392f54afc146107f957806393b8098714610819576103c3565b806349590657116102e257806369b91432116102755780637d54f248116102445780637d54f2481461075a5780638a554abe1461077a5780638d2a88881461079a5780638da5cb5b146107ba576103c3565b806369b91432146106fb578063715018a6146107105780637bb96acb146107255780637d3636011461073a576103c3565b806354fd4d50116102b157806354fd4d50146106795780636008cd1f1461069b5780636133f985146106bb57806363f8071c146106db576103c3565b8063495906571461060d5780634a14cd84146106225780634e71e0c8146106445780635322843014610659576103c3565b806328342ecf1161035a578063327e965e11610329578063327e965e14610587578063438c2a42146105a75780634597d3ce146105d457806347b67d05146105fa576103c3565b806328342ecf146105075780632c3d356b146105275780632d80caa5146105545780632fa5825f14610567576103c3565b80630ec2e821116103965780630ec2e8211461046d578063150b7a021461048d5780631ef36835146104ba57806328068da3146104e7576103c3565b806301b1eb07146103c85780630394bc2b146103ea57806305987d571461040a57806309824a8014610440575b600080fd5b3480156103d457600080fd5b506103e86103e3366004614af3565b610b38565b005b3480156103f657600080fd5b506103e8610405366004614781565b610c7e565b34801561041657600080fd5b5061042a6104253660046147bf565b610d51565b60405161043791906150ce565b60405180910390f35b34801561044c57600080fd5b5061046061045b3660046143b4565b610de4565b6040516104379190615ada565b34801561047957600080fd5b506103e86104883660046143b4565b610f26565b34801561049957600080fd5b506104ad6104a8366004614816565b61109f565b6040516104379190615110565b3480156104c657600080fd5b506104da6104d5366004614500565b6110c8565b6040516104379190614efc565b3480156104f357600080fd5b506103e8610502366004614424565b61114a565b34801561051357600080fd5b506103e86105223660046143b4565b6112b8565b34801561053357600080fd5b50610547610542366004614be3565b611431565b60405161043791906150c3565b6103e86105623660046143b4565b611471565b34801561057357600080fd5b5061042a6105823660046143ec565b61154f565b34801561059357600080fd5b506103e86105a23660046149f4565b61159b565b3480156105b357600080fd5b506105c76105c23660046143ec565b611661565b6040516104379190615b38565b3480156105e057600080fd5b506105e96116bb565b604051610437959493929190615afa565b6103e86106083660046148e7565b6116fe565b34801561061957600080fd5b5061042a6117e2565b34801561062e57600080fd5b506106376117e8565b6040516104379190615ae9565b34801561065057600080fd5b506103e86117f4565b34801561066557600080fd5b506103e8610674366004614ab3565b6118db565b34801561068557600080fd5b5061068e6119c0565b604051610437919061513d565b3480156106a757600080fd5b506105476106b6366004614931565b6119f7565b3480156106c757600080fd5b506103e86106d636600461469b565b611a2f565b3480156106e757600080fd5b506104606106f63660046143b4565b611c42565b34801561070757600080fd5b5061042a611c57565b34801561071c57600080fd5b506103e8611c68565b34801561073157600080fd5b5061042a611d28565b34801561074657600080fd5b506105c7610755366004614781565b611d2e565b34801561076657600080fd5b506103e86107753660046146db565b611dbf565b34801561078657600080fd5b506105476107953660046143b4565b611edf565b3480156107a657600080fd5b506103e86107b53660046143ec565b611fca565b3480156107c657600080fd5b506104da61205e565b3480156107db57600080fd5b5061054761207a565b3480156107f057600080fd5b506104da612086565b34801561080557600080fd5b50610547610814366004614be3565b6120a2565b34801561082557600080fd5b506103e8610834366004614b2b565b612103565b34801561084557600080fd5b50610637610854366004614bab565b6121cd565b34801561086557600080fd5b506103e8612304565b34801561087a57600080fd5b506108836124ac565b6040516104379190615593565b34801561089c57600080fd5b506103e86108ab3660046143ec565b61253f565b3480156108bc57600080fd5b506104da612789565b6103e86108d3366004614474565b6127a5565b3480156108e457600080fd5b506108f86108f3366004614b7b565b61286f565b6040516104379190615554565b34801561091157600080fd5b506104ad6109203660046145f1565b6128cf565b34801561093157600080fd5b506105476128f9565b34801561094657600080fd5b5061042a6109553660046143b4565b612905565b34801561096657600080fd5b506103e861292d565b34801561097b57600080fd5b506103e861098a36600461498b565b612b9d565b34801561099b57600080fd5b506103e86109aa36600461498b565b612c35565b6103e86109bd36600461495c565b612e55565b3480156109ce57600080fd5b5061042a6109dd3660046143b4565b612f29565b3480156109ee57600080fd5b506103e86109fd366004614c2d565b613060565b348015610a0e57600080fd5b506103e8610a1d366004614931565b61317f565b348015610a2e57600080fd5b506103e8610a3d366004614570565b61329b565b348015610a4e57600080fd5b506103e8610a5d366004614c00565b6134b6565b348015610a6e57600080fd5b506104da61360a565b348015610a8357600080fd5b5061042a613626565b348015610a9857600080fd5b506104da610aa7366004614b43565b61362c565b348015610ab857600080fd5b506104ad610ac7366004614880565b6136d2565b348015610ad857600080fd5b506103e8610ae73660046143b4565b6136fc565b348015610af857600080fd5b5061042a61380a565b348015610b0d57600080fd5b506105476138b1565b348015610b2257600080fd5b50610b2b613a0e565b6040516104379190615b2a565b60025415610b7b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60405180910390fd5b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314610bd1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b60195460ff75010000000000000000000000000000000000000000009091041615158115151415610c2e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061529a565b601980549115157501000000000000000000000000000000000000000000027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff9092169190911790556000600255565b60025415610cb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517feaacf5e40000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899063eaacf5e490610d1690600390889088908890889060040161570f565b60006040518083038186803b158015610d2e57600080fd5b505af4158015610d42573d6000803e3d6000fd5b50506000600255505050505050565b73ffffffffffffffffffffffffffffffffffffffff8086166000908152601b60209081526040808320938516835292905290812081856001811115610d9257fe5b6001811115610d9d57fe5b81526020808201929092526040908101600090812073ffffffffffffffffffffffffffffffffffffffff891682528352818120868252909252902054905095945050505050565b6000600254600014610e22576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314610e78576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517f36d626e90000000000000000000000000000000000000000000000000000000081527324f51fadc255dfcbc436615167c325cac7010b29906336d626e990610ecb906003908690600401615604565b60206040518083038186803b158015610ee357600080fd5b505af4158015610ef7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b9190614b5f565b600060025592915050565b60025415610f60576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314610fb6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff8116611003576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b60085473ffffffffffffffffffffffffffffffffffffffff1615611053576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061522c565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790556000600255565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6000806110d6600386613a2f565b73ffffffffffffffffffffffffffffffffffffffff80891660009081526013602090815260408083208b85168452825280832061ffff90951683529381528382206bffffffffffffffffffffffff89168352815283822063ffffffff88168352905291909120541691505095945050505050565b60025415611184576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002558361119381611edf565b6111c9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6019547501000000000000000000000000000000000000000000900460ff1661121e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615308565b6008546040517ff18d03cc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063f18d03cc9061127a908890889088908890600401614f95565b600060405180830381600087803b15801561129457600080fd5b505af11580156112a8573d6000803e3d6000fd5b5050600060025550505050505050565b600254156112f2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314611348576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff8116611395576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b60075473ffffffffffffffffffffffffffffffffffffffff16156113e5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061522c565b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790556000600255565b60008061143f600384613a2f565b63ffffffff85166000908152600e6020908152604080832061ffff9094168352929052205460ff169150505b92915050565b600254156114ab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560006114bd600383613a2f565b6040517ebaadff0000000000000000000000000000000000000000000000000000000081529091507346a71e786c34714928390782e9e50ee1c2fa99899062baadff906115169060039060009086908290600401615628565b60006040518083038186803b15801561152e57600080fd5b505af4158015611542573d6000803e3d6000fd5b5050600060025550505050565b60008061155d600384613a2f565b73ffffffffffffffffffffffffffffffffffffffff85166000908152600f6020908152604080832061ffff9094168352929052205491505092915050565b600254156115d5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517fef22a02f0000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899063ef22a02f9061163590600390899089908990899089906004016157c1565b60006040518083038186803b15801561164d57600080fd5b505af41580156112a8573d6000803e3d6000fd5b60008061166f600384613a2f565b73ffffffffffffffffffffffffffffffffffffffff8516600090815260116020908152604080832061ffff909416835292905220546bffffffffffffffffffffffff1691505092915050565b60155463ffffffff81169160ff64010000000083048116926501000000000081048216926601000000000000820483169267010000000000000090920490911690565b60025415611738576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002558261174781611edf565b61177d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b600061178a600385613a2f565b6040517ebaadff0000000000000000000000000000000000000000000000000000000081529091507346a71e786c34714928390782e9e50ee1c2fa99899062baadff9061163590600390899086908990600401615628565b60095490565b60035463ffffffff1690565b60015473ffffffffffffffffffffffffffffffffffffffff163314611845576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6001546000805460405173ffffffffffffffffffffffffffffffffffffffff93841693909116917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a360018054600080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff841617909155169055565b60025415611915576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff16331461196b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517f5ee5495b0000000000000000000000000000000000000000000000000000000081527394cdf6621c190f5950ed9e16debb19e6a8d2e84e90635ee5495b90611516906003908690869060040161585f565b60408051808201909152600581527f332e362e30000000000000000000000000000000000000000000000000000000602082015290565b73ffffffffffffffffffffffffffffffffffffffff919091166000908152601260209081526040808320938352929052205460ff1690565b60025415611a69576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560195473ffffffffffffffffffffffffffffffffffffffff16158015611a945750600954155b611aca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615150565b73ffffffffffffffffffffffffffffffffffffffff8216611b17576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b6000805473ffffffffffffffffffffffffffffffffffffffff8085167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790925560198054928616929091169190911790556040805160a081019091526011606082019081527f4c6f6f7072696e672050726f746f636f6c00000000000000000000000000000060808301528152730e3229004b55ed8edcc29473c790f33c2b693d3890633aa277299060039086908590611be99060208101611bdb6119c0565b815230602090910152613aba565b6040518563ffffffff1660e01b8152600401611c089493929190615757565b60006040518083038186803b158015611c2057600080fd5b505af4158015611c34573d6000803e3d6000fd5b505060006002555050505050565b6000611c4f600383613a2f565b90505b919050565b6000611c636003613b31565b905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314611cb9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6000805460405173ffffffffffffffffffffffffffffffffffffffff909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600b5490565b73ffffffffffffffffffffffffffffffffffffffff84166000908152601a6020526040812081846001811115611d6057fe5b6001811115611d6b57fe5b81526020808201929092526040908101600090812073ffffffffffffffffffffffffffffffffffffffff8816825283528181208582529092529020546bffffffffffffffffffffffff169050949350505050565b60025415611df9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025587611e0881611edf565b611e3e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517f74417afc0000000000000000000000000000000000000000000000000000000081527307f50b5a6a3f1f069c27ea11ffa62b53a20872cc906374417afc90611e9f906003908d908d908d908d908d908d908d908d9060040161568f565b60006040518083038186803b158015611eb757600080fd5b505af4158015611ecb573d6000803e3d6000fd5b505060006002555050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8216331480611c4f575060075473ffffffffffffffffffffffffffffffffffffffff1615801590611c4f57506007546040517f4ebf59d200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690634ebf59d290611f7a9085903390600401614f1d565b60206040518083038186803b158015611f9257600080fd5b505afa158015611fa6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4f9190614b0f565b60025415612004576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517f95d755550000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa9989906395d75555906115169060039086908690600401615663565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b6000611c636003613b42565b60075473ffffffffffffffffffffffffffffffffffffffff1690565b6000806120b0600384613a2f565b63ffffffff8516600090815260106020908152604080832061ffff9094168352929052205467ffffffffffffffff7401000000000000000000000000000000000000000090910416151591505092915050565b6002541561213d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517fcae3c7810000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899063cae3c781906121959060039085906004016159eb565b60006040518083038186803b1580156121ad57600080fd5b505af41580156121c1573d6000803e3d6000fd5b50506000600255505050565b600060025460001461220b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314612261576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517f1854b85c00000000000000000000000000000000000000000000000000000000815273b9c5d84f8fc65aa921d306e37f2dba9b19ec389190631854b85c906122b4906003908690600401615ac6565b60206040518083038186803b1580156122cc57600080fd5b505af41580156122e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b9190614bc7565b6002541561233e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025561234d6003613b42565b156124a5576005546040517f883515c100000000000000000000000000000000000000000000000000000000815260009173ffffffffffffffffffffffffffffffffffffffff169063883515c1906123a9903090600401614efc565b60206040518083038186803b1580156123c157600080fd5b505afa1580156123d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f99190614b93565b6005546040517fc51c820800000000000000000000000000000000000000000000000000000000815291925073ffffffffffffffffffffffffffffffffffffffff169063c51c8208906124509084906004016150ce565b602060405180830381600087803b15801561246a57600080fd5b505af115801561247e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a29190614b93565b50505b6000600255565b6124b46140cd565b6040518061014001604052807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001815260200161100081526020016213c680815260200162093a808152602001640100000000815260200162010000815260200162093a80815260200162278d008152602001604481526020016213c68063ffffffff16815250905090565b60025415612579576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff1633146125cf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff811661261c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615187565b60085473ffffffffffffffffffffffffffffffffffffffff83811691161415612671576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615263565b73ffffffffffffffffffffffffffffffffffffffff82166126b757476126b0815a73ffffffffffffffffffffffffffffffffffffffff85169190613b4b565b50506124a2565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8416906370a082319061270c903090600401614efc565b60206040518083038186803b15801561272457600080fd5b505afa158015612738573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275c9190614b93565b905061277f73ffffffffffffffffffffffffffffffffffffffff84168383613bae565b5050506000600255565b60085473ffffffffffffffffffffffffffffffffffffffff1690565b600254156127df576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b6001600255856127ee81611edf565b612824576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b611c348787878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060039796959493925050613bbf9050565b612877614120565b506000908152600a602090815260409182902082518084019093525463ffffffff811683526401000000009004811b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000169082015290565b7fbc197c810000000000000000000000000000000000000000000000000000000095945050505050565b6000611c636003613e49565b73ffffffffffffffffffffffffffffffffffffffff1660009081526018602052604090205490565b60025415612967576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff1633146129bd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b600554604080517f8d80b252000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff1691638d80b252916004808301926020929190829003018186803b158015612a2857600080fd5b505afa158015612a3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a6091906143d0565b73ffffffffffffffffffffffffffffffffffffffff161415612aae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b600554604080517f8d80b252000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff90921691638d80b25291600480820192602092909190829003018186803b158015612b1957600080fd5b505afa158015612b2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b5191906143d0565b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790556000600255565b60025415612bd7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b60016002556040517ff460cca90000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899063f460cca990610d16906003908890889088908890600401615788565b60025415612c6f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b6001600255828114612cad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061551d565b6007546040517f2eac95f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690632eac95f190612d0790879087903390600401615049565b60206040518083038186803b158015612d1f57600080fd5b505afa158015612d33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d579190614b0f565b612d8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b60005b838110156121c157600160126000878785818110612daa57fe5b9050602002016020810190612dbf91906143b4565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000858585818110612e0757fe5b6020908102929092013583525081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055600101612d90565b60025415612e8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025582612e9e81611edf565b612ed4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517ebaadff0000000000000000000000000000000000000000000000000000000081527346a71e786c34714928390782e9e50ee1c2fa99899062baadff90610d1690600390889088908890600401615628565b6000600254600014612f67576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314612fbd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b6040517ff93234a200000000000000000000000000000000000000000000000000000000815273b9c5d84f8fc65aa921d306e37f2dba9b19ec38919063f93234a290613010906003908690600401615604565b60206040518083038186803b15801561302857600080fd5b505af415801561303c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b9190614b93565b6002541561309a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff1633146130f0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b60c88160ff16111561312e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906154e6565b6019805460ff90921674010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff9092169190911790556000600255565b600254156131b9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b6001600255816131c881611edf565b6131fe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff831660009081526012602090815260408083208584529091529081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f7115b98931e7aca6d0aa11d10fe28877316a661a44c4bfc93c76b19dbbf5b107906132899085908590615023565b60405180910390a15050600060025550565b600254156132d5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b6001600255856132e481611edf565b61331a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff8216613367576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061551d565b6000613374600387613a2f565b73ffffffffffffffffffffffffffffffffffffffff89811660009081526013602090815260408083208c85168452825280832061ffff8616845282528083206bffffffffffffffffffffffff8b168452825280832063ffffffff8a1684529091529020549192501615613413576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615489565b73ffffffffffffffffffffffffffffffffffffffff9788166000908152601360209081526040808320998b16835298815288822061ffff90931682529182528781206bffffffffffffffffffffffff909616815294815286852063ffffffff9094168552929092529382208054959094167fffffffffffffffffffffffff0000000000000000000000000000000000000000909516949094179092555060025550565b600254156134f0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025563ffffffff8216600090815260106020908152604080832061ffff851684529091529020805474010000000000000000000000000000000000000000900467ffffffffffffffff16613573576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615452565b805467ffffffffffffffff74010000000000000000000000000000000000000000909104166213c680014210156135d6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615452565b4260178190556040517f5b8f5ce93a49fc6eab534327f9c77fd2966e16278d6135cc0d99e6b6450c796391613289916150ce565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b60045490565b6040517ff24789a80000000000000000000000000000000000000000000000000000000081526000907324f51fadc255dfcbc436615167c325cac7010b299063f24789a890613682906003908690600401615ab4565b60206040518083038186803b15801561369a57600080fd5b505af41580156136ae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4f91906143d0565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461374d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b73ffffffffffffffffffffffffffffffffffffffff81161580159061378d575060005473ffffffffffffffffffffffffffffffffffffffff828116911614155b6137c3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615187565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6005546040517f883515c100000000000000000000000000000000000000000000000000000000815260009173ffffffffffffffffffffffffffffffffffffffff169063883515c190613861903090600401614efc565b60206040518083038186803b15801561387957600080fd5b505afa15801561388d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c639190614b93565b60006002546000146138ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151be565b600160025560005473ffffffffffffffffffffffffffffffffffffffff163314613945576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906151f5565b61394f6003613b42565b15613986576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7290615376565b6139906003613e49565b156139c7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906153ad565b4260168190556040517f574214b195bf5273a95bb4498e35cf1fde0ce327c727a95ec2ab359f7ba4e11a916139fb916150ce565b60405180910390a1506001600060025590565b60195474010000000000000000000000000000000000000000900460ff1690565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600a8301602052604090205461ffff1680613a92576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906153e4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f836000015180519060200120846020015180519060200120838660400151604051602001613b139594939291906150d7565b60405160208183030381529060405280519060200120915050919050565b6011015463ffffffff166110000390565b60140154151590565b6000613b6e73ffffffffffffffffffffffffffffffffffffffff85168484613e52565b905080613ba7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906152d1565b9392505050565b613bba8383835a613ef9565b505050565b73ffffffffffffffffffffffffffffffffffffffff8416613c0c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061533f565b6000613c188785613a2f565b60058801546040517ff3d790b700000000000000000000000000000000000000000000000000000000815291925060009173ffffffffffffffffffffffffffffffffffffffff9091169063f3d790b7903490613c7e908b908a908a908a90600401614fcc565b6020604051808303818588803b158015613c9757600080fd5b505af1158015613cab573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613cd09190614c47565b73ffffffffffffffffffffffffffffffffffffffff87166000908152600e8a016020908152604080832061ffff871684528252918290208251808401909352546bffffffffffffffffffffffff168083524267ffffffffffffffff169183019190915291925090613d419083613f41565b6bffffffffffffffffffffffff908116825273ffffffffffffffffffffffffffffffffffffffff88166000908152600e8b016020908152604080832061ffff8816845282529182902084518154928601517fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009093169416939093177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff166c0100000000000000000000000067ffffffffffffffff9092169190910217909155517f73ff7b101bcdc22f199e8e1dd9893170a683d6897be4f1086ca05705abb886ae90613e36908a908a908a9088908890614f44565b60405180910390a1505050505050505050565b60130154151590565b600082613e6157506001613ba7565b6000613e828573ffffffffffffffffffffffffffffffffffffffff16613f8f565b90508073ffffffffffffffffffffffffffffffffffffffff16848490604051613eaa90613f8f565b600060405180830381858888f193505050503d8060008114613ee8576040519150601f19603f3d011682016040523d82523d6000602084013e613eed565b606091505b50909695505050505050565b613f0584848484613f92565b613f3b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b72906152d1565b50505050565b8181016bffffffffffffffffffffffff808416908216101561146b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b729061541b565b90565b60008063a9059cbb60e01b8585604051602401613fb0929190615023565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008673ffffffffffffffffffffffffffffffffffffffff1684836040516140379190614ee0565b60006040518083038160008787f1925050503d8060008114614075576040519150601f19603f3d011682016040523d82523d6000602084013e61407a565b606091505b5050905061408781614092565b979650505050505050565b600081156140c9573d80156140b257602081146140bb57600092506140c7565b600192506140c7565b60206000803e60005192505b505b5090565b604051806101400160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604080518082019091526000808252602082015290565b60008083601f840112614148578182fd5b50813567ffffffffffffffff81111561415f578182fd5b602083019150836020808302850101111561417957600080fd5b9250929050565b600082601f830112614190578081fd5b813560206141a56141a083615b75565b615b51565b82815281810190858301838502870184018810156141c1578586fd5b855b858110156141e85781356141d681615c22565b845292840192908401906001016141c3565b5090979650505050505050565b600082601f830112614205578081fd5b813560206142156141a083615b75565b8281528181019085830183850287018401881015614231578586fd5b855b858110156141e8576142448261437e565b84529284019290840190600101614233565b600082601f830112614266578081fd5b813560206142766141a083615b75565b8281528181019085830183850287018401881015614292578586fd5b855b858110156141e857813584529284019290840190600101614294565b8035611c5281615c47565b60008083601f8401126142cc578182fd5b50813567ffffffffffffffff8111156142e3578182fd5b60208301915083602082850101111561417957600080fd5b600082601f83011261430b578081fd5b813567ffffffffffffffff81111561431f57fe5b61435060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601615b51565b818152846020838601011115614364578283fd5b816020850160208301379081016020019190915292915050565b803560028110611c5257600080fd5b8035611c5281615c55565b8035611c5281615c65565b803560ff81168114611c5257600080fd5b6000602082840312156143c5578081fd5b8135613ba781615c22565b6000602082840312156143e1578081fd5b8151613ba781615c22565b600080604083850312156143fe578081fd5b823561440981615c22565b9150602083013561441981615c22565b809150509250929050565b60008060008060808587031215614439578182fd5b843561444481615c22565b9350602085013561445481615c22565b9250604085013561446481615c22565b9396929550929360600135925050565b60008060008060008060a0878903121561448c578384fd5b863561449781615c22565b955060208701356144a781615c22565b945060408701356144b781615c22565b935060608701356144c781615c77565b9250608087013567ffffffffffffffff8111156144e2578283fd5b6144ee89828a016142bb565b979a9699509497509295939492505050565b600080600080600060a08688031215614517578283fd5b853561452281615c22565b9450602086013561453281615c22565b9350604086013561454281615c22565b9250606086013561455281615c77565b9150608086013561456281615c65565b809150509295509295909350565b60008060008060008060c08789031215614588578384fd5b863561459381615c22565b955060208701356145a381615c22565b945060408701356145b381615c22565b935060608701356145c381615c77565b925060808701356145d381615c65565b915060a08701356145e381615c22565b809150509295509295509295565b600080600080600060a08688031215614608578283fd5b853561461381615c22565b9450602086013561462381615c22565b9350604086013567ffffffffffffffff8082111561463f578485fd5b61464b89838a01614256565b94506060880135915080821115614660578283fd5b61466c89838a01614256565b93506080880135915080821115614681578283fd5b5061468e888289016142fb565b9150509295509295909350565b6000806000606084860312156146af578081fd5b83356146ba81615c22565b925060208401356146ca81615c22565b929592945050506040919091013590565b60008060008060008060008060e0898b0312156146f6578586fd5b883561470181615c22565b9750602089013561471181615c22565b965061471f60408a0161437e565b9550606089013561472f81615c22565b94506080890135935060a089013561474681615c77565b925060c089013567ffffffffffffffff811115614761578283fd5b61476d8b828c016142bb565b999c989b5096995094979396929594505050565b60008060008060808587031215614796578182fd5b84356147a181615c22565b935060208501356147b181615c22565b92506144646040860161437e565b600080600080600060a086880312156147d6578283fd5b85356147e181615c22565b945060208601356147f181615c22565b93506147ff6040870161437e565b925060608601359150608086013561456281615c22565b6000806000806080858703121561482b578182fd5b843561483681615c22565b9350602085013561484681615c22565b925060408501359150606085013567ffffffffffffffff811115614868578182fd5b614874878288016142fb565b91505092959194509250565b600080600080600060a08688031215614897578283fd5b85356148a281615c22565b945060208601356148b281615c22565b93506040860135925060608601359150608086013567ffffffffffffffff8111156148db578182fd5b61468e888289016142fb565b6000806000606084860312156148fb578081fd5b833561490681615c22565b9250602084013561491681615c22565b9150604084013561492681615c65565b809150509250925092565b60008060408385031215614943578182fd5b823561494e81615c22565b946020939093013593505050565b600080600060608486031215614970578081fd5b833561497b81615c22565b9250602084013561491681615c55565b600080600080604085870312156149a0578182fd5b843567ffffffffffffffff808211156149b7578384fd5b6149c388838901614137565b909650945060208701359150808211156149db578384fd5b506149e887828801614137565b95989497509550505050565b600080600080600060a08688031215614a0b578283fd5b853567ffffffffffffffff80821115614a22578485fd5b614a2e89838a01614180565b96506020880135915080821115614a43578485fd5b614a4f89838a01614180565b95506040880135915080821115614a64578485fd5b614a7089838a016141f5565b94506060880135915080821115614a85578283fd5b614a9189838a01614180565b93506080880135915080821115614aa6578283fd5b5061468e88828901614256565b60008060208385031215614ac5578182fd5b823567ffffffffffffffff811115614adb578283fd5b614ae785828601614137565b90969095509350505050565b600060208284031215614b04578081fd5b8135613ba781615c47565b600060208284031215614b20578081fd5b8151613ba781615c47565b6000610ae08284031215614b3d578081fd5b50919050565b600060208284031215614b54578081fd5b8135613ba781615c55565b600060208284031215614b70578081fd5b8151613ba781615c55565b600060208284031215614b8c578081fd5b5035919050565b600060208284031215614ba4578081fd5b5051919050565b600060208284031215614bbc578081fd5b8135613ba781615c65565b600060208284031215614bd8578081fd5b8151613ba781615c65565b60008060408385031215614bf5578182fd5b823561440981615c65565b60008060408385031215614c12578182fd5b8235614c1d81615c65565b9150602083013561441981615c55565b600060208284031215614c3e578081fd5b613ba7826143a3565b600060208284031215614c58578081fd5b8151613ba781615c77565b60008284526020808501945082825b85811015614cad578135614c8581615c22565b73ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614c72565b509495945050505050565b6000815180845260208085019450808401835b83811015614cad57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614ccb565b61030080828437600081840152505050565b61060080828437600081840152505050565b61010080828437600081840152505050565b6000815180845260208085019450808401835b83811015614cad57815187529582019590820190600101614d46565b15159052565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b60008151808452614dc8816020860160208601615bf6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60028110614e0457fe5b9052565b8035614e1381615c55565b61ffff1682526020810135614e2781615c77565b6bffffffffffffffffffffffff16602083015260408181013590830152606090810135910152565b8035614e5a81615c22565b73ffffffffffffffffffffffffffffffffffffffff8082168452614e806020840161437e565b9150614e8f6020850183614dfa565b60408301359150614e9f82615c22565b1660408301526060818101359083015260ff614ebd608083016143a3565b1660808301525050565b61ffff169052565b63ffffffff169052565b60ff169052565b60008251614ef2818460208701615bf6565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9586168152938516602085015291909316604083015261ffff90921660608201526bffffffffffffffffffffffff909116608082015260a00190565b73ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152506bffffffffffffffffffffffff84166040830152608060608301526150196080830184614db0565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b6040808252810183905260008460608301825b8681101561509957823561506f81615c22565b73ffffffffffffffffffffffffffffffffffffffff1682526020928301929091019060010161505c565b50809250505073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b901515815260200190565b90815260200190565b94855260208501939093526040840191909152606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00190565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b600060208252613ba76020830184614db0565b6020808252600b908201527f494e495449414c495a4544000000000000000000000000000000000000000000604082015260600190565b6020808252600f908201527f494e56414c49445f414444524553530000000000000000000000000000000000604082015260600190565b6020808252600a908201527f5245454e5452414e435900000000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f554e415554484f52495a45440000000000000000000000000000000000000000604082015260600190565b6020808252600b908201527f414c52454144595f534554000000000000000000000000000000000000000000604082015260600190565b6020808252600d908201527f494e56414c49445f544f4b454e00000000000000000000000000000000000000604082015260600190565b6020808252600a908201527f53414d455f56414c554500000000000000000000000000000000000000000000604082015260600190565b60208082526010908201527f5452414e534645525f4641494c55524500000000000000000000000000000000604082015260600190565b6020808252600b908201527f4e4f545f414c4c4f574544000000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f5a45524f5f414444524553530000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f494e56414c49445f4d4f44450000000000000000000000000000000000000000604082015260600190565b60208082526010908201527f414c52454144595f53485554444f574e00000000000000000000000000000000604082015260600190565b6020808252600f908201527f544f4b454e5f4e4f545f464f554e440000000000000000000000000000000000604082015260600190565b6020808252600c908201527f4144445f4f564552464c4f570000000000000000000000000000000000000000604082015260600190565b60208082526016908201527f5749544844524157414c5f4e4f545f544f4f5f4f4c4400000000000000000000604082015260600190565b60208082526021908201527f43414e4e4f545f4f564552524944455f524543495049454e545f41444452455360408201527f5300000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252600d908201527f494e56414c49445f56414c554500000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f494e56414c49445f444154410000000000000000000000000000000000000000604082015260600190565b815163ffffffff1681526020918201517fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000169181019190915260400190565b600061014082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525092915050565b91825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b93845273ffffffffffffffffffffffffffffffffffffffff92909216602084015261ffff16604083015263ffffffff16606082015260800190565b92835273ffffffffffffffffffffffffffffffffffffffff918216602084015216604082015260600190565b60006101008b835273ffffffffffffffffffffffffffffffffffffffff808c166020850152808b1660408501526156c9606085018b614dfa565b8089166080850152508660a08401526bffffffffffffffffffffffff861660c08401528060e08401526156ff8184018587614d68565b9c9b505050505050505050505050565b85815273ffffffffffffffffffffffffffffffffffffffff85811660208301528416604082015260a081016157476060830185614dfa565b8260808301529695505050505050565b93845273ffffffffffffffffffffffffffffffffffffffff9290921660208401526040830152606082015260800190565b6000868252606060208301526157a2606083018688614c63565b82810360408401526157b5818587614c63565b98975050505050505050565b6000878252602060c0818401526157db60c0840189614cb8565b83810360408501526157ed8189614cb8565b84810360608601528751808252838901925090830190845b8181101561582857615818838551614dfa565b9284019291840191600101615805565b5050848103608086015261583c8188614cb8565b9250505082810360a08401526158528185614d33565b9998505050505050505050565b83815260406020808301829052828201849052600091906060908185018682028601830188865b898110156159db577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa089840301845281357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe218c36030181126158e6578889fd5b8b016101e06158fd856158f8846143a3565b614ed9565b61590887830161438d565b61591488870182614ec7565b506159208983016143a3565b61592c8a870182614ed9565b5061593988830183615b93565b828a88015261594b8388018284614d68565b92505050608061595f818701828501614d21565b5061018061596e8184016142b0565b61597a82880182614d62565b50506101a061598b81840184615b93565b8784038389015261599d848284614d68565b93505050506101c06159b181840184615b93565b9350868303828801526159c5838583614d68565b9789019796505050928601925050600101615886565b50909a9950505050505050505050565b828152610b00810182356159fe81615c65565b63ffffffff81166020840152506020830135615a1981615c22565b73ffffffffffffffffffffffffffffffffffffffff81166040840152506040830135606083015260608301356080830152615a5660808401614398565b615a6360a0840182614ecf565b5060a083013560c0830152615a7e60e0830160c08501614e08565b615a9061016083016101408501614e4f565b615aa261020083016101e08501614d0f565b613ba761080083016107e08501614cfd565b91825261ffff16602082015260400190565b91825263ffffffff16602082015260400190565b61ffff91909116815260200190565b63ffffffff91909116815260200190565b63ffffffff95909516855260ff938416602086015291831660408501528216606084015216608082015260a00190565b60ff91909116815260200190565b6bffffffffffffffffffffffff91909116815260200190565b60405181810167ffffffffffffffff81118282101715615b6d57fe5b604052919050565b600067ffffffffffffffff821115615b8957fe5b5060209081020190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615bc7578283fd5b830160208101925035905067ffffffffffffffff811115615be757600080fd5b80360383131561417957600080fd5b60005b83811015615c11578181015183820152602001615bf9565b83811115613f3b5750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114615c4457600080fd5b50565b8015158114615c4457600080fd5b61ffff81168114615c4457600080fd5b63ffffffff81168114615c4457600080fd5b6bffffffffffffffffffffffff81168114615c4457600080fdfea264697066735822122090e1d0b4b5978ee2e8ccae3378a2c50a98e4f9a781297a63e9886657ead8f53664736f6c63430007060033
Libraries Used
ExchangeDeposits : 0x07f50b5a6a3f1f069c27ea11ffa62b53a20872ccExchangeAdmins : 0xb9c5d84f8fc65aa921d306e37f2dba9b19ec3891ExchangeBlocks : 0x94cdf6621c190f5950ed9e16debb19e6a8d2e84eExchangeTokens : 0x24f51fadc255dfcbc436615167c325cac7010b29ExchangeGenesis : 0x0e3229004b55ed8edcc29473c790f33c2b693d38ExchangeWithdrawals : 0x46a71e786c34714928390782e9e50ee1c2fa9989
Deployed Bytecode Sourcemap
271500:19705:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;290959:243;;;;;;;;;;-1:-1:-1;290959:243:0;;;;;:::i;:::-;;:::i;:::-;;283670:414;;;;;;;;;;-1:-1:-1;283670:414:0;;;;;:::i;:::-;;:::i;285237:403::-;;;;;;;;;;-1:-1:-1;285237:403:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;277184:232;;;;;;;;;;-1:-1:-1;277184:232:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;274323:399::-;;;;;;;;;;-1:-1:-1;274323:399:0;;;;;:::i;:::-;;:::i;16858:164::-;;;;;;;;;;-1:-1:-1;16858:164:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;287035:386::-;;;;;;;;;;-1:-1:-1;287035:386:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;287429:370::-;;;;;;;;;;-1:-1:-1;287429:370:0;;;;;:::i;:::-;;:::i;273500:342::-;;;;;;;;;;-1:-1:-1;273500:342:0;;;;;:::i;:::-;;:::i;283089:305::-;;;;;;;;;;-1:-1:-1;283089:305:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;282485:291::-;;;;;;:::i;:::-;;:::i;284943:286::-;;;;;;;;;;-1:-1:-1;284943:286:0;;;;;:::i;:::-;;:::i;284396:539::-;;;;;;;;;;-1:-1:-1;284396:539:0;;;;;:::i;:::-;;:::i;280781:306::-;;;;;;;;;;-1:-1:-1;280781:306:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;289944:630::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;281808:342::-;;;;;;:::i;:::-;;:::i;279005:149::-;;;;;;;;;;;;;:::i;289345:188::-;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;26958:205::-;;;;;;;;;;;;;:::i;279513:187::-;;;;;;;;;;-1:-1:-1;279513:187:0;;;;;:::i;:::-;;:::i;272648:120::-;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;288632:245::-;;;;;;;;;;-1:-1:-1;288632:245:0;;;;;:::i;:::-;;:::i;272950:542::-;;;;;;;;;;-1:-1:-1;272950:542:0;;;;;:::i;:::-;;:::i;277424:199::-;;;;;;;;;;-1:-1:-1;277424:199:0;;;;;:::i;:::-;;:::i;279708:177::-;;;;;;;;;;;;;:::i;25739:161::-;;;;;;;;;;;;;:::i;279162:146::-;;;;;;;;;;;;;:::i;281095:367::-;;;;;;;;;;-1:-1:-1;281095:367:0;;;;;:::i;:::-;;:::i;280281:492::-;;;;;;;;;;-1:-1:-1;280281:492:0;;;;;:::i;:::-;;:::i;275706:272::-;;;;;;;;;;-1:-1:-1;275706:272:0;;;;;:::i;:::-;;:::i;283402:260::-;;;;;;;;;;-1:-1:-1;283402:260:0;;;;;:::i;:::-;;:::i;24799:20::-;;;;;;;;;;;;;:::i;276839:161::-;;;;;;;;;;;;;:::i;274153:162::-;;;;;;;;;;;;;:::i;282158:319::-;;;;;;;;;;-1:-1:-1;282158:319:0;;;;;:::i;:::-;;:::i;282859:222::-;;;;;;;;;;-1:-1:-1;282859:222:0;;;;;:::i;:::-;;:::i;289074:263::-;;;;;;;;;;-1:-1:-1;289074:263:0;;;;;:::i;:::-;;:::i;278540:436::-;;;;;;;;;;;;;:::i;276010:802::-;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;274906:792::-;;;;;;;;;;-1:-1:-1;274906:792:0;;;;;:::i;:::-;;:::i;274730:168::-;;;;;;;;;;;;;:::i;279918:355::-;;;;;;:::i;:::-;;:::i;279316:189::-;;;;;;;;;;-1:-1:-1;279316:189:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;15251:203::-;;;;;;;;;;-1:-1:-1;15251:203:0;;;;;:::i;:::-;;:::i;277008:145::-;;;;;;;;;;;;;:::i;278296:236::-;;;;;;;;;;-1:-1:-1;278296:236:0;;;;;:::i;:::-;;:::i;273850:295::-;;;;;;;;;;;;;:::i;284092:296::-;;;;;;;;;;-1:-1:-1;284092:296:0;;;;;:::i;:::-;;:::i;288136:488::-;;;;;;;;;;-1:-1:-1;288136:488:0;;;;;:::i;:::-;;:::i;281498:302::-;;;;;;:::i;:::-;;:::i;278046:240::-;;;;;;;;;;-1:-1:-1;278046:240:0;;;;;:::i;:::-;;:::i;290582:219::-;;;;;;;;;;-1:-1:-1;290582:219:0;;;;;:::i;:::-;;:::i;287807:321::-;;;;;;;;;;-1:-1:-1;287807:321:0;;;;;:::i;:::-;;:::i;286390:637::-;;;;;;;;;;-1:-1:-1;286390:637:0;;;;;:::i;:::-;;:::i;285648:734::-;;;;;;;;;;-1:-1:-1;285648:734:0;;;;;:::i;:::-;;:::i;26258:27::-;;;;;;;;;;;;;:::i;288885:160::-;;;;;;;;;;;;;:::i;277631:199::-;;;;;;;;;;-1:-1:-1;277631:199:0;;;;;:::i;:::-;;:::i;15068:175::-;;;;;;;;;;-1:-1:-1;15068:175:0;;;;;:::i;:::-;;:::i;26631:247::-;;;;;;;;;;-1:-1:-1;26631:247:0;;;;;:::i;:::-;;:::i;277859:179::-;;;;;;;;;;;;;:::i;289541:395::-;;;;;;;;;;;;;:::i;290809:142::-;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;290959:243::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;;;;;;;;;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;291091:30:::0;;::::2;::::0;;;::::2;;:39;;::::0;::::2;;;;291083:62;;;;;;;;;;;;:::i;:::-;291156:30:::0;:38;;;::::2;;::::0;::::2;::::0;;;::::2;::::0;;;::::2;::::0;;-1:-1:-1;11977:11:0;:15;290959:243::o;283670:414::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;283948:128:::1;::::0;;;;:35:::1;::::0;::::1;::::0;:128:::1;::::0;:5:::1;::::0;283998;;284018;;284038:7;;284060:5;;283948:128:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;11991:1:0;11977:11;:15;-1:-1:-1;;;;;;283670:414:0:o;285237:403::-;285567:34;;;;285538:4;285567:34;;;:27;:34;;;;;;;;:42;;;;;;;;;;;285538:4;285610:7;285567:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;285567:51:0;;;:58;;;;;;;;;;:65;;;;;;;;;;-1:-1:-1;285237:403:0;;;;;;;:::o;277184:232::-;277344:6;11790:11;;11805:1;11790:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;277375:33:::2;::::0;;;;:19:::2;::::0;::::2;::::0;:33:::2;::::0;:5:::2;::::0;277395:12;;277375:33:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;11991:1:::0;11977:11;:15;277368:40;277184:232;-1:-1:-1;;277184:232:0:o;274323:399::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;274478:30:::2;::::0;::::2;274470:55;;;;;;;;;;;;:::i;:::-;274585:21:::0;;:44:::2;:21;:44:::0;274577:68:::2;;;;;;;;;;;;:::i;:::-;274656:21:::0;:58;;;::::2;;::::0;;;::::2;::::0;;;::::2;::::0;;-1:-1:-1;11977:11:0;:15;274323:399::o;16858:164::-;16984:30;16858:164;;;;;;:::o;287035:386::-;287267:7;;287309:23;:5;287326;287309:16;:23::i;:::-;287350:31;;;;;;;;:25;:31;;;;;;;;:35;;;;;;;;;;:44;;;;;;;;;;;;:52;;;;;;;;;;:63;;;;;;;;;;;;;;-1:-1:-1;;287035:386:0;;;;;;;:::o;287429:370::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;287649:4;272495:20:::1;287649:4:::0;272495:13:::1;:20::i;:::-;272487:45;;;;;;;;;;;;:::i;:::-;287679:30:::0;;;;::::2;;;287671:54;;;;;;;;;;;;:::i;:::-;287736:21:::0;;:55:::2;::::0;;;;:21:::2;::::0;;::::2;::::0;:30:::2;::::0;:55:::2;::::0;287767:4;;287773:2;;287777:5;;287784:6;;287736:55:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;;11991:1:0;11977:11;:15;-1:-1:-1;;;;;;;287429:370:0:o;273500:342::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;273651:28:::2;::::0;::::2;273643:53;;;;;;;;;;;;:::i;:::-;273715:19:::0;;:40:::2;:19;:40:::0;273707:64:::2;;;;;;;;;;;;:::i;:::-;273782:19:::0;:52;;;::::2;;::::0;;;::::2;::::0;;;::::2;::::0;;-1:-1:-1;11977:11:0;:15;273500:342::o;283089:305::-;283257:4;;283296:23;:5;283313;283296:16;:23::i;:::-;283337:40;;;;;;;:29;:40;;;;;;;;:49;;;;;;;;;;;;;;-1:-1:-1;;283089:305:0;;;;;:::o;282485:291::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;282641:14:::1;282658:23;:5;282675::::0;282658:16:::1;:23::i;:::-;282692:76;::::0;;;;282641:40;;-1:-1:-1;282692:19:0::1;::::0;::::1;::::0;:76:::1;::::0;:5:::1;::::0;282720:1:::1;::::0;282641:40;;282720:1;;282692:76:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;11991:1:0;11977:11;:15;-1:-1:-1;;;;282485:291:0:o;284943:286::-;285101:4;;285140:23;:5;285157;285140:16;:23::i;:::-;285181:31;;;;;;;:24;:31;;;;;;;;:40;;;;;;;;;;;;-1:-1:-1;;284943:286:0;;;;:::o;284396:539::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;284768:159:::1;::::0;;;;:40:::1;::::0;::::1;::::0;:159:::1;::::0;:5:::1;::::0;284823:6;;284844:7;;284866:8;;284889:6;;284910;;284768:159:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;280781:306:::0;280946:6;;280987:30;:5;281004:12;280987:16;:30::i;:::-;281035:28;;;;;;;:21;:28;;;;;;;;:37;;;;;;;;;;:44;;;;-1:-1:-1;;280781:306:0;;;;:::o;289944:630::-;290264:21;:30;;;;;290320:34;;;;;;;290380;;;;;;290448:42;;;;;;290524;;;;;;;;289944:630::o;281808:342::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;282022:5;272495:20:::1;282022:5:::0;272495:13:::1;:20::i;:::-;272487:45;;;;;;;;;;;;:::i;:::-;282045:14:::2;282062:23;:5;282079::::0;282062:16:::2;:23::i;:::-;282096:46;::::0;;;;282045:40;;-1:-1:-1;282096:19:0::2;::::0;::::2;::::0;:46:::2;::::0;:5:::2;::::0;282116;;282045:40;;282132:9;;282096:46:::2;;;:::i;279005:149::-:0;279130:16;;279005:149;:::o;289345:188::-;289489:5;:36;;;289345:188;:::o;26958:205::-;26439:12;;;;26425:10;:26;26417:51;;;;;;;;;;;;:::i;:::-;27075:12:::1;::::0;::::1;27068:5:::0;;27047:41:::1;::::0;27075:12:::1;::::0;;::::1;::::0;27068:5;;::::1;::::0;27047:41:::1;::::0;::::1;27107:12;::::0;;::::1;27099:20:::0;;;;;::::1;27107:12;::::0;::::1;27099:20;::::0;;;27130:25:::1;::::0;;26958:205::o;279513:187::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;279666:26:::2;::::0;;;;:18:::2;::::0;::::2;::::0;:26:::2;::::0;:5:::2;::::0;279685:6;;;;279666:26:::2;;;:::i;272648:120::-:0;272746:14;;;;;;;;;;;;;;;;;272648:120;:::o;288632:245::-;288829:23;;;;;288800:4;288829:23;;;:16;:23;;;;;;;;:40;;;;;;;;;;;288632:245::o;272950:542::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;272294:18;;:32:::1;:18;:32:::0;:66;::::1;;;-1:-1:-1::0;272330:16:0;;:30;272294:66:::1;272272:127;;;;;;;;;;;;:::i;:::-;273184:20:::2;::::0;::::2;273176:45;;;;;;;;;;;;:::i;:::-;273232:5;:14:::0;;::::2;::::0;;::::2;::::0;;;::::2;;::::0;;;273257:18;:30;;;;::::2;::::0;;;::::2;::::0;;;::::2;::::0;;273412:60:::2;::::0;;;;;;;;::::2;;::::0;::::2;::::0;;;::::2;::::0;;;;;;273300:28:::2;::::0;::::2;::::0;273257:5:::2;::::0;273278:9;;273367:18;;273400:73:::2;::::0;273412:60:::2;::::0;::::2;273447:9;:7;:9::i;:::-;273412:60:::0;;273466:4:::2;273412:60;::::0;;::::2;::::0;273400:11:::2;:73::i;:::-;273300:184;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;;11991:1:0;11977:11;:15;-1:-1:-1;;;;;272950:542:0:o;277424:199::-;277554:6;277585:30;:5;277602:12;277585:16;:30::i;:::-;277578:37;;277424:199;;;;:::o;279708:177::-;279814:4;279843:34;:5;:32;:34::i;:::-;279836:41;;279708:177;:::o;25739:161::-;25245:5;;;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;25860:1:::1;25845:5:::0;;25824:39:::1;::::0;::::1;25845:5:::0;;::::1;::::0;25824:39:::1;::::0;25860:1;;25824:39:::1;25890:1;25874:18:::0;;;::::1;::::0;;25739:161::o;279162:146::-;279285:15;;279162:146;:::o;281095:367::-;281393:31;;;281362:6;281393:31;;;:24;:31;;;;;281362:6;281425:7;281393:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;281393:40:0;;;:47;;;;;;;;;;:54;;;;;;;;:61;;;;-1:-1:-1;281095:367:0;;;;;;:::o;280281:492::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;280668:4;272495:20:::1;280668:4:::0;272495:13:::1;:20::i;:::-;272487:45;;;;;;;;;;;;:::i;:::-;280690:75:::2;::::0;;;;:16:::2;::::0;::::2;::::0;:75:::2;::::0;:5:::2;::::0;280707:4;;280713:2;;280717:7;;280726:12;;280740:5;;280747:6;;280755:9;;;;280690:75:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;;11991:1:0;11977:11;:15;-1:-1:-1;;;;;;;;;;;280281:492:0:o;275706:272::-;275792:4;275822:19;;;275831:10;275822:19;;:148;;-1:-1:-1;275858:19:0;;:49;:19;:49;;;;:112;;-1:-1:-1;275924:19:0;;:46;;;;;:19;;;;;:27;;:46;;275952:5;;275959:10;;275924:19;:46;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;283402:260::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;283571:83:::1;::::0;;;;:32:::1;::::0;::::1;::::0;:83:::1;::::0;:5:::1;::::0;283618;;283638;;283571:83:::1;;;:::i;24799:20::-:0;;;;;;:::o;276839:161::-;276937:4;276966:26;:5;:24;:26::i;274153:162::-;274288:19;;;;274153:162;:::o;282158:319::-;282324:4;;282363:23;:5;282380;282363:16;:23::i;:::-;282404:41;;;;;;;:30;:41;;;;;;;;:50;;;;;;;;;;:60;;;;;;;:65;;;-1:-1:-1;;282158:319:0;;;;:::o;282859:222::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;283032:41:::1;::::0;;;;:28:::1;::::0;::::1;::::0;:41:::1;::::0;:5:::1;::::0;283061:11;;283032:41:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;11991:1:0;11977:11;:15;-1:-1:-1;;;282859:222:0:o;289074:263::-;289249:6;11790:11;;11805:1;11790:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;289280:49:::2;::::0;;;;:39:::2;::::0;::::2;::::0;:49:::2;::::0;:5:::2;::::0;289320:8;;289280:49:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;278540:436::-:0;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;278745:26:::1;:5;:24;:26::i;:::-;278741:228;;;278857:14:::0;;:46:::1;::::0;;;;278844:10:::1;::::0;278857:14:::1;;::::0;:31:::1;::::0;:46:::1;::::0;278897:4:::1;::::0;278857:46:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;278918:14:::0;;:39:::1;::::0;;;;278844:59;;-1:-1:-1;278918:14:0::1;;::::0;:32:::1;::::0;:39:::1;::::0;278844:59;;278918:39:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;278741:228;;11991:1:::0;11977:11;:15;278540:436::o;276010:802::-;276101:29;;:::i;:::-;276155:649;;;;;;;;43811:77;276155:649;;;;43945:4;276155:649;;;;44022:7;276155:649;;;;44097:6;276155:649;;;;44150:7;276155:649;;;;44202:7;276155:649;;;;44275:6;276155:649;;;;44332:7;276155:649;;;;44715:2;276155:649;;;;44595:7;276729:64;;276155:649;;;276148:656;;276010:802;:::o;274906:792::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;275100:23:::2;::::0;::::2;275092:51;;;;;;;;;;;;:::i;:::-;275346:21:::0;;::::2;275329:39:::0;;::::2;275346:21:::0;::::2;275329:39;;275321:65;;;;;;;;;;;;:::i;:::-;275403:19;::::0;::::2;275399:292;;275453:21;275489:45;275453:21:::0;275524:9:::2;275489:26;::::0;::::2;::::0;:45;:26:::2;:45::i;:::-;;275399:292;;;;275581:37;::::0;;;;275567:11:::2;::::0;275581:22:::2;::::0;::::2;::::0;::::2;::::0;:37:::2;::::0;275612:4:::2;::::0;275581:37:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;275567:51:::0;-1:-1:-1;275633:46:0::2;:27;::::0;::::2;275661:9:::0;275567:51;275633:27:::2;:46::i;:::-;275399:292;-1:-1:-1::0;;11991:1:0;11977:11;:15;274906:792::o;274730:168::-;274869:21;;;;274730:168;:::o;279918:355::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;280187:4;272495:20:::1;280187:4:::0;272495:13:::1;:20::i;:::-;272487:45;;;;;;;;;;;;:::i;:::-;280209:56:::2;280223:4;280229:2;280233:12;280247:6;280255:9;;280209:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;::::0;;;;-1:-1:-1;280209:5:0::2;::::0;:56;;;;;-1:-1:-1;;280209:13:0::2;:56:::0;-1:-1:-1;280209:56:0:i:2;279316:189::-:0;279421:29;;:::i;:::-;-1:-1:-1;279475:22:0;;;;:12;:22;;;;;;;;;279468:29;;;;;;;;;;;;;;;;;;;;;;;;;;279316:189::o;15251:203::-;15410:36;15251:203;;;;;;;:::o;277008:145::-;277098:4;277127:18;:5;:16;:18::i;278296:236::-;278476:48;;278447:4;278476:48;;;:34;:48;;;;;;;278296:236::o;273850:295::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;273983:14:::0;;:37:::2;::::0;;;;;;;274032:1:::2;::::0;273983:51:::2;:14;::::0;:35:::2;::::0;:37:::2;::::0;;::::2;::::0;::::2;::::0;;;;;;;;:14;:37;::::2;;::::0;::::2;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:51;;;;273975:76;;;;;;;;;;;;:::i;:::-;274099:14:::0;;:37:::2;::::0;;;;;;;:14:::2;::::0;;::::2;::::0;:35:::2;::::0;:37:::2;::::0;;::::2;::::0;::::2;::::0;;;;;;;;;:14;:37;::::2;;::::0;::::2;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;274062:19:::0;:75;;;::::2;;::::0;;;::::2;::::0;;;::::2;::::0;;-1:-1:-1;11977:11:0;:15;273850:295::o;284092:296::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;284290:90:::1;::::0;;;;:37:::1;::::0;::::1;::::0;:90:::1;::::0;:5:::1;::::0;284342:6;;;;284363;;;;284290:90:::1;;;:::i;288136:488::-:0;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;288341:41;;::::1;288333:66;;;;;;;;;;;;:::i;:::-;288418:19:::0;;:47:::1;::::0;;;;:19:::1;::::0;;::::1;::::0;:27:::1;::::0;:47:::1;::::0;288446:6;;;;288454:10:::1;::::0;288418:19:::1;:47;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;288410:72;;;;;;;;;;;;:::i;:::-;288498:6;288493:124;288510:17:::0;;::::1;288493:124;;;288601:4;288549:16:::0;:27:::1;288566:6:::0;;288573:1;288566:9;;::::1;;;;;;;;;;;;;;;;;;:::i;:::-;288549:27;;;;;;;;;;;;;;;:49;288577:17;;288595:1;288577:20;;;;;;;;::::0;;::::1;::::0;;;::::1;;288549:49:::0;;-1:-1:-1;288549:49:0;::::1;::::0;;;;;;-1:-1:-1;288549:49:0;:56;;;::::1;::::0;::::1;;::::0;;;::::1;::::0;;-1:-1:-1;288529:3:0::1;288493:124;;281498:302:::0;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;281723:5;272495:20:::1;281723:5:::0;272495:13:::1;:20::i;:::-;272487:45;;;;;;;;;;;;:::i;:::-;281746:46:::2;::::0;;;;:19:::2;::::0;::::2;::::0;:46:::2;::::0;:5:::2;::::0;281766;;281773:7;;281782:9;;281746:46:::2;;;:::i;278046:240::-:0;278211:4;11790:11;;11805:1;11790:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;278240:38:::2;::::0;;;;:27:::2;::::0;::::2;::::0;:38:::2;::::0;:5:::2;::::0;278268:9;;278240:38:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;290582:219::-:0;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;290734:3:::2;290722:8;:15;;;;290714:41;;;;;;;;;;;;:::i;:::-;290766:16:::0;:27;;::::2;::::0;;::::2;::::0;::::2;::::0;;;::::2;::::0;;;::::2;::::0;;-1:-1:-1;11977:11:0;:15;290582:219::o;287807:321::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;287991:5;272495:20:::1;287991:5:::0;272495:13:::1;:20::i;:::-;272487:45;;;;;;;;;;;;:::i;:::-;288014:23:::2;::::0;::::2;;::::0;;;:16;:23:::2;::::0;;;;;;;:40;;;;;;;;;;:47;;;::::2;288057:4;288014:47;::::0;;288077:43;::::2;::::0;::::2;::::0;288031:5;;288038:15;;288077:43:::2;:::i;:::-;;;;;;;;-1:-1:-1::0;;11991:1:0;11977:11;:15;-1:-1:-1;287807:321:0:o;286390:637::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;286672:4;272495:20:::1;286672:4:::0;272495:13:::1;:20::i;:::-;272487:45;;;;;;;;;;;;:::i;:::-;286702:26:::2;::::0;::::2;286694:51;;;;;;;;;;;;:::i;:::-;286756:14;286773:23;:5;286790::::0;286773:16:::2;:23::i;:::-;286815:77;:31:::0;;::::2;286890:1;286815:31:::0;;;:25;:31:::2;::::0;;;;;;;:35;;::::2;::::0;;;;;;;:44:::2;::::0;::::2;::::0;;;;;;;:52:::2;::::0;::::2;::::0;;;;;;;:63:::2;::::0;::::2;::::0;;;;;;;;286756:40;;-1:-1:-1;286815:63:0::2;:77:::0;286807:123:::2;;;;;;;;;;;;:::i;:::-;286941:31;::::0;;::::2;;::::0;;;:25;:31:::2;::::0;;;;;;;:35;;::::2;::::0;;;;;;;;:44:::2;::::0;;::::2;::::0;;;;;;;;:52:::2;::::0;;::::2;::::0;;;;;;;;:63:::2;::::0;;::::2;::::0;;;;;;;;;:78;;;;;::::2;::::0;;;::::2;::::0;;;::::2;::::0;;;-1:-1:-1;11977:11:0;:15;-1:-1:-1;286390:637:0:o;285648:734::-;11790:11;;:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;285871:41:::1;::::0;::::1;285820:48;285871:41:::0;;;:30;:41:::1;::::0;;;;;;;:50:::1;::::0;::::1;::::0;;;;;;;285940:20;;;;::::1;;;285932:60;;;;;;;;;;;;:::i;:::-;286103:20:::0;;::::1;::::0;;;::::1;;44022:7;286103:78;286084:15;:97;;286076:132;;;;;;;;;;;;:::i;:::-;286287:15;286255:29:::0;:47;;;286320:54:::1;::::0;::::1;::::0;::::1;::::0;::::1;:::i;26258:27::-:0;;;;;;:::o;288885:160::-;289015:22;;288885:160;:::o;277631:199::-;277792:30;;;;;277760:7;;277792:21;;;;:30;;:5;;277814:7;;277792:30;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;15068:175::-;15204:31;15068:175;;;;;;;:::o;26631:247::-;25245:5;;;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;26773:22:::1;::::0;::::1;::::0;;::::1;::::0;:43:::1;;-1:-1:-1::0;26811:5:0::1;::::0;::::1;26799:17:::0;;::::1;26811:5:::0;::::1;26799:17;;26773:43;26765:71;;;;;;;;;;;;:::i;:::-;26847:12;:23:::0;;;::::1;;::::0;;;::::1;::::0;;;::::1;::::0;;26631:247::o;277859:179::-;277984:14;;:46;;;;;277955:4;;277984:14;;;:31;;:46;;278024:4;;277984:46;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;289541:395::-;289656:12;11790:11;;11805:1;11790:16;11782:39;;;;;;;;;;;;:::i;:::-;11892:1;11878:11;:15;25245:5:::1;::::0;::::1;;25231:10;:19;25223:44;;;;;;;;;;;;:::i;:::-;289695:26:::2;:5;:24;:26::i;:::-;289694:27;289686:52;;;;;;;;;;;;:::i;:::-;289758:18;:5;:16;:18::i;:::-;289757:19;289749:48;;;;;;;;;;;;:::i;:::-;289838:15;289808:27:::0;:45;;;289869:37:::2;::::0;::::2;::::0;::::2;::::0;::::2;:::i;:::-;;;;;;;;-1:-1:-1::0;289924:4:0::2;11991:1:::0;11977:11;:15;289541:395;:::o;290809:142::-;290927:16;;;;;;;;290809:142::o;139041:330::-;139250:30;;;139208:14;139250:30;;;:16;;;:30;;;;;;;;139299:12;139291:40;;;;;;;;;;;;:::i;:::-;139352:11;;;139041:330;-1:-1:-1;;139041:330:0:o;4054:466::-;4140:7;4165:13;4212:9;4200:21;;3874:111;4352:6;:11;;;4336:29;;;;;;4400:6;:14;;;4384:32;;;;;;4435:8;4462:6;:24;;;4266:235;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;4242:270;;;;;;4235:277;;;4054:466;;;:::o;81086:243::-;81291:30;;;;;43945:4;81251:70;;81086:243::o;80653:218::-;80834:25;;;:29;;;80653:218::o;1908:269::-;2054:12;2094:28;:10;;;2105:6;2113:8;2094:10;:28::i;:::-;2084:38;;2141:7;2133:36;;;;;;;;;;;;:::i;:::-;1908:269;;;;;:::o;5145:278::-;5290:125;5338:5;5358:2;5375:5;5395:9;5290:33;:125::i;:::-;5145:278;;;:::o;266031:1604::-;266410:16;;;266402:41;;;;;;;;;;;;:::i;:::-;266838:14;266855:26;:1;266868:12;266855;:26::i;:::-;266968:17;;;;:145;;;;;266838:43;;-1:-1:-1;266943:22:0;;266968:17;;;;;:25;;267001:9;;266968:145;;267026:4;;267045:12;;267072:6;;267093:9;;266968:145;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;267265:21;;;267226:36;267265:21;;;:17;;;:21;;;;;;;;:30;;;;;;;;;;;267226:69;;;;;;;;;;;;;;267334:15;267226:69;267306:44;267226:69;;;267306:44;;;;266943:170;;-1:-1:-1;267226:69:0;267379:36;;266943:170;267379:19;:36::i;:::-;267361:54;;;;;;267426:21;;;267361:15;267426:21;;;:17;;;:21;;;;;;;;:30;;;;;;;;;;;:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;267485:142;;;;;267516:4;;267426:21;;267552:12;;267426:30;;267601:15;;267485:142;:::i;:::-;;;;;;;;266031:1604;;;;;;;;;:::o;80879:199::-;81043:23;;;:27;;;80879:199::o;1358:395::-;1495:12;1529:11;1525:55;;-1:-1:-1;1564:4:0;1557:11;;1525:55;1590:25;1618:14;:2;:12;;;:14::i;:::-;1590:42;;1697:9;:14;;1719:6;1732:8;1697:48;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1683:62:0;;1358:395;-1:-1:-1;;;;;;1358:395:0:o;5730:310::-;5936:52;5961:5;5968:2;5972:5;5979:8;5936:24;:52::i;:::-;5914:118;;;;;;;;;;;;:::i;:::-;5730:310;;;;:::o;187273:197::-;187415:5;;;187439:6;;;;;;;;;187431:31;;;;;;;;;;;;:::i;1043:164::-;1194:4;1043:164::o;6048:800::-;6225:4;6605:21;6673:10;6666:18;;6699:2;6716:5;6629:103;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6605:127;;6744:12;6762:5;:10;;6778:8;6788;6762:35;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6743:54;;;6815:25;6832:7;6815:16;:25::i;:::-;6808:32;6048:800;-1:-1:-1;;;;;;;6048:800:0:o;8836:1127::-;8946:4;9269:7;9265:666;;;9328:16;9483:61;;;;9661:2;9656:115;;;;9885:1;9874:12;;9321:584;;9483:61;9524:1;9513:12;;9483:61;;9656:115;9708:2;9705:1;9702;9687:24;9750:1;9744:8;9733:19;;9321:584;;9302:618;-1:-1:-1;9948:7:0;8836:1127::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;14:404:1:-;;;147:3;140:4;132:6;128:17;124:27;114:2;;172:8;162;155:26;114:2;-1:-1:-1;202:20:1;;245:18;234:30;;231:2;;;284:8;274;267:26;231:2;328:4;320:6;316:17;304:29;;391:3;384:4;376;368:6;364:17;356:6;352:30;348:41;345:50;342:2;;;408:1;405;398:12;342:2;104:314;;;;;:::o;423:782::-;;536:3;529:4;521:6;517:17;513:27;503:2;;558:5;551;544:20;503:2;598:6;585:20;624:4;648:65;663:49;709:2;663:49;:::i;:::-;648:65;:::i;:::-;747:15;;;778:12;;;;810:15;;;856:11;;;844:24;;840:33;;837:42;-1:-1:-1;834:2:1;;;896:5;889;882:20;834:2;922:5;936:240;950:2;947:1;944:9;936:240;;;1021:3;1008:17;1038:33;1065:5;1038:33;:::i;:::-;1084:18;;1122:12;;;;1154;;;;968:1;961:9;936:240;;;-1:-1:-1;1194:5:1;;493:712;-1:-1:-1;;;;;;;493:712:1:o;1210:725::-;;1329:3;1322:4;1314:6;1310:17;1306:27;1296:2;;1351:5;1344;1337:20;1296:2;1391:6;1378:20;1417:4;1441:65;1456:49;1502:2;1456:49;:::i;1441:65::-;1540:15;;;1571:12;;;;1603:15;;;1649:11;;;1637:24;;1633:33;;1630:42;-1:-1:-1;1627:2:1;;;1689:5;1682;1675:20;1627:2;1715:5;1729:177;1743:2;1740:1;1737:9;1729:177;;;1800:31;1827:3;1800:31;:::i;:::-;1788:44;;1852:12;;;;1884;;;;1761:1;1754:9;1729:177;;1940:705;;2053:3;2046:4;2038:6;2034:17;2030:27;2020:2;;2075:5;2068;2061:20;2020:2;2115:6;2102:20;2141:4;2165:65;2180:49;2226:2;2180:49;:::i;2165:65::-;2264:15;;;2295:12;;;;2327:15;;;2373:11;;;2361:24;;2357:33;;2354:42;-1:-1:-1;2351:2:1;;;2413:5;2406;2399:20;2351:2;2439:5;2453:163;2467:2;2464:1;2461:9;2453:163;;;2524:17;;2512:30;;2562:12;;;;2594;;;;2485:1;2478:9;2453:163;;2650:132;2717:20;;2746:30;2717:20;2746:30;:::i;2787:377::-;;;2904:3;2897:4;2889:6;2885:17;2881:27;2871:2;;2929:8;2919;2912:26;2871:2;-1:-1:-1;2959:20:1;;3002:18;2991:30;;2988:2;;;3041:8;3031;3024:26;2988:2;3085:4;3077:6;3073:17;3061:29;;3137:3;3130:4;3121:6;3113;3109:19;3105:30;3102:39;3099:2;;;3154:1;3151;3144:12;3169:601;;3266:3;3259:4;3251:6;3247:17;3243:27;3233:2;;3288:5;3281;3274:20;3233:2;3328:6;3315:20;3354:18;3350:2;3347:26;3344:2;;;3376:9;3344:2;3411:113;3518:4;3449:66;3442:4;3438:2;3434:13;3430:86;3426:97;3411:113;:::i;:::-;3549:2;3540:7;3533:19;3595:3;3588:4;3583:2;3575:6;3571:15;3567:26;3564:35;3561:2;;;3616:5;3609;3602:20;3561:2;3685;3678:4;3670:6;3666:17;3659:4;3650:7;3646:18;3633:55;3708:16;;;3726:4;3704:27;3697:42;;;;3712:7;3223:547;-1:-1:-1;;3223:547:1:o;3775:151::-;3851:20;;3900:1;3890:12;;3880:2;;3916:1;3913;3906:12;3931:136;4000:20;;4029:32;4000:20;4029:32;:::i;4072:136::-;4141:20;;4170:32;4141:20;4170:32;:::i;4213:158::-;4281:20;;4341:4;4330:16;;4320:27;;4310:2;;4361:1;4358;4351:12;4376:259;;4488:2;4476:9;4467:7;4463:23;4459:32;4456:2;;;4509:6;4501;4494:22;4456:2;4553:9;4540:23;4572:33;4599:5;4572:33;:::i;4640:263::-;;4763:2;4751:9;4742:7;4738:23;4734:32;4731:2;;;4784:6;4776;4769:22;4731:2;4821:9;4815:16;4840:33;4867:5;4840:33;:::i;4908:402::-;;;5037:2;5025:9;5016:7;5012:23;5008:32;5005:2;;;5058:6;5050;5043:22;5005:2;5102:9;5089:23;5121:33;5148:5;5121:33;:::i;:::-;5173:5;-1:-1:-1;5230:2:1;5215:18;;5202:32;5243:35;5202:32;5243:35;:::i;:::-;5297:7;5287:17;;;4995:315;;;;;:::o;5315:614::-;;;;;5478:3;5466:9;5457:7;5453:23;5449:33;5446:2;;;5500:6;5492;5485:22;5446:2;5544:9;5531:23;5563:33;5590:5;5563:33;:::i;:::-;5615:5;-1:-1:-1;5672:2:1;5657:18;;5644:32;5685:35;5644:32;5685:35;:::i;:::-;5739:7;-1:-1:-1;5798:2:1;5783:18;;5770:32;5811:35;5770:32;5811:35;:::i;:::-;5436:493;;;;-1:-1:-1;5865:7:1;;5919:2;5904:18;5891:32;;-1:-1:-1;;5436:493:1:o;5934:997::-;;;;;;;6132:3;6120:9;6111:7;6107:23;6103:33;6100:2;;;6154:6;6146;6139:22;6100:2;6198:9;6185:23;6217:33;6244:5;6217:33;:::i;:::-;6269:5;-1:-1:-1;6326:2:1;6311:18;;6298:32;6339:35;6298:32;6339:35;:::i;:::-;6393:7;-1:-1:-1;6452:2:1;6437:18;;6424:32;6465:35;6424:32;6465:35;:::i;:::-;6519:7;-1:-1:-1;6578:2:1;6563:18;;6550:32;6591:34;6550:32;6591:34;:::i;:::-;6644:7;-1:-1:-1;6702:3:1;6687:19;;6674:33;6730:18;6719:30;;6716:2;;;6767:6;6759;6752:22;6716:2;6811:60;6863:7;6854:6;6843:9;6839:22;6811:60;:::i;:::-;6090:841;;;;-1:-1:-1;6090:841:1;;-1:-1:-1;6090:841:1;;6890:8;;6090:841;-1:-1:-1;;;6090:841:1:o;6936:829::-;;;;;;7114:3;7102:9;7093:7;7089:23;7085:33;7082:2;;;7136:6;7128;7121:22;7082:2;7180:9;7167:23;7199:33;7226:5;7199:33;:::i;:::-;7251:5;-1:-1:-1;7308:2:1;7293:18;;7280:32;7321:35;7280:32;7321:35;:::i;:::-;7375:7;-1:-1:-1;7434:2:1;7419:18;;7406:32;7447:35;7406:32;7447:35;:::i;:::-;7501:7;-1:-1:-1;7560:2:1;7545:18;;7532:32;7573:34;7532:32;7573:34;:::i;:::-;7626:7;-1:-1:-1;7685:3:1;7670:19;;7657:33;7699:34;7657:33;7699:34;:::i;:::-;7752:7;7742:17;;;7072:693;;;;;;;;:::o;7770:973::-;;;;;;;7965:3;7953:9;7944:7;7940:23;7936:33;7933:2;;;7987:6;7979;7972:22;7933:2;8031:9;8018:23;8050:33;8077:5;8050:33;:::i;:::-;8102:5;-1:-1:-1;8159:2:1;8144:18;;8131:32;8172:35;8131:32;8172:35;:::i;:::-;8226:7;-1:-1:-1;8285:2:1;8270:18;;8257:32;8298:35;8257:32;8298:35;:::i;:::-;8352:7;-1:-1:-1;8411:2:1;8396:18;;8383:32;8424:34;8383:32;8424:34;:::i;:::-;8477:7;-1:-1:-1;8536:3:1;8521:19;;8508:33;8550:34;8508:33;8550:34;:::i;:::-;8603:7;-1:-1:-1;8662:3:1;8647:19;;8634:33;8676:35;8634:33;8676:35;:::i;:::-;8730:7;8720:17;;;7923:820;;;;;;;;:::o;8748:1129::-;;;;;;8987:3;8975:9;8966:7;8962:23;8958:33;8955:2;;;9009:6;9001;8994:22;8955:2;9053:9;9040:23;9072:33;9099:5;9072:33;:::i;:::-;9124:5;-1:-1:-1;9181:2:1;9166:18;;9153:32;9194:35;9153:32;9194:35;:::i;:::-;9248:7;-1:-1:-1;9306:2:1;9291:18;;9278:32;9329:18;9359:14;;;9356:2;;;9391:6;9383;9376:22;9356:2;9419:67;9478:7;9469:6;9458:9;9454:22;9419:67;:::i;:::-;9409:77;;9539:2;9528:9;9524:18;9511:32;9495:48;;9568:2;9558:8;9555:16;9552:2;;;9589:6;9581;9574:22;9552:2;9617:69;9678:7;9667:8;9656:9;9652:24;9617:69;:::i;:::-;9607:79;;9739:3;9728:9;9724:19;9711:33;9695:49;;9769:2;9759:8;9756:16;9753:2;;;9790:6;9782;9775:22;9753:2;;9818:53;9863:7;9852:8;9841:9;9837:24;9818:53;:::i;:::-;9808:63;;;8945:932;;;;;;;;:::o;9882:470::-;;;;10028:2;10016:9;10007:7;10003:23;9999:32;9996:2;;;10049:6;10041;10034:22;9996:2;10093:9;10080:23;10112:33;10139:5;10112:33;:::i;:::-;10164:5;-1:-1:-1;10221:2:1;10206:18;;10193:32;10234:35;10193:32;10234:35;:::i;:::-;9986:366;;10288:7;;-1:-1:-1;;;10342:2:1;10327:18;;;;10314:32;;9986:366::o;10357:1161::-;;;;;;;;;10601:3;10589:9;10580:7;10576:23;10572:33;10569:2;;;10623:6;10615;10608:22;10569:2;10667:9;10654:23;10686:33;10713:5;10686:33;:::i;:::-;10738:5;-1:-1:-1;10795:2:1;10780:18;;10767:32;10808:35;10767:32;10808:35;:::i;:::-;10862:7;-1:-1:-1;10888:46:1;10930:2;10915:18;;10888:46;:::i;:::-;10878:56;;10986:2;10975:9;10971:18;10958:32;10999:35;11026:7;10999:35;:::i;:::-;11053:7;-1:-1:-1;11107:3:1;11092:19;;11079:33;;-1:-1:-1;11164:3:1;11149:19;;11136:33;11178:34;11136:33;11178:34;:::i;:::-;11231:7;-1:-1:-1;11289:3:1;11274:19;;11261:33;11317:18;11306:30;;11303:2;;;11354:6;11346;11339:22;11303:2;11398:60;11450:7;11441:6;11430:9;11426:22;11398:60;:::i;:::-;10559:959;;;;-1:-1:-1;10559:959:1;;-1:-1:-1;10559:959:1;;;;;;11477:8;-1:-1:-1;;;10559:959:1:o;11523:565::-;;;;;11698:3;11686:9;11677:7;11673:23;11669:33;11666:2;;;11720:6;11712;11705:22;11666:2;11764:9;11751:23;11783:33;11810:5;11783:33;:::i;:::-;11835:5;-1:-1:-1;11892:2:1;11877:18;;11864:32;11905:35;11864:32;11905:35;:::i;:::-;11959:7;-1:-1:-1;11985:46:1;12027:2;12012:18;;11985:46;:::i;12093:709::-;;;;;;12285:3;12273:9;12264:7;12260:23;12256:33;12253:2;;;12307:6;12299;12292:22;12253:2;12351:9;12338:23;12370:33;12397:5;12370:33;:::i;:::-;12422:5;-1:-1:-1;12479:2:1;12464:18;;12451:32;12492:35;12451:32;12492:35;:::i;:::-;12546:7;-1:-1:-1;12572:46:1;12614:2;12599:18;;12572:46;:::i;:::-;12562:56;;12665:2;12654:9;12650:18;12637:32;12627:42;;12721:3;12710:9;12706:19;12693:33;12735:35;12762:7;12735:35;:::i;12807:691::-;;;;;12979:3;12967:9;12958:7;12954:23;12950:33;12947:2;;;13001:6;12993;12986:22;12947:2;13045:9;13032:23;13064:33;13091:5;13064:33;:::i;:::-;13116:5;-1:-1:-1;13173:2:1;13158:18;;13145:32;13186:35;13145:32;13186:35;:::i;:::-;13240:7;-1:-1:-1;13294:2:1;13279:18;;13266:32;;-1:-1:-1;13349:2:1;13334:18;;13321:32;13376:18;13365:30;;13362:2;;;13413:6;13405;13398:22;13362:2;13441:51;13484:7;13475:6;13464:9;13460:22;13441:51;:::i;:::-;13431:61;;;12937:561;;;;;;;:::o;13503:760::-;;;;;;13692:3;13680:9;13671:7;13667:23;13663:33;13660:2;;;13714:6;13706;13699:22;13660:2;13758:9;13745:23;13777:33;13804:5;13777:33;:::i;:::-;13829:5;-1:-1:-1;13886:2:1;13871:18;;13858:32;13899:35;13858:32;13899:35;:::i;:::-;13953:7;-1:-1:-1;14007:2:1;13992:18;;13979:32;;-1:-1:-1;14058:2:1;14043:18;;14030:32;;-1:-1:-1;14113:3:1;14098:19;;14085:33;14141:18;14130:30;;14127:2;;;14178:6;14170;14163:22;14127:2;14206:51;14249:7;14240:6;14229:9;14225:22;14206:51;:::i;14268:543::-;;;;14413:2;14401:9;14392:7;14388:23;14384:32;14381:2;;;14434:6;14426;14419:22;14381:2;14478:9;14465:23;14497:33;14524:5;14497:33;:::i;:::-;14549:5;-1:-1:-1;14606:2:1;14591:18;;14578:32;14619:35;14578:32;14619:35;:::i;:::-;14673:7;-1:-1:-1;14732:2:1;14717:18;;14704:32;14745:34;14704:32;14745:34;:::i;:::-;14798:7;14788:17;;;14371:440;;;;;:::o;14816:327::-;;;14945:2;14933:9;14924:7;14920:23;14916:32;14913:2;;;14966:6;14958;14951:22;14913:2;15010:9;14997:23;15029:33;15056:5;15029:33;:::i;:::-;15081:5;15133:2;15118:18;;;;15105:32;;-1:-1:-1;;;14903:240:1:o;15148:541::-;;;;15292:2;15280:9;15271:7;15267:23;15263:32;15260:2;;;15313:6;15305;15298:22;15260:2;15357:9;15344:23;15376:33;15403:5;15376:33;:::i;:::-;15428:5;-1:-1:-1;15485:2:1;15470:18;;15457:32;15498:34;15457:32;15498:34;:::i;15694:815::-;;;;;15893:2;15881:9;15872:7;15868:23;15864:32;15861:2;;;15914:6;15906;15899:22;15861:2;15959:9;15946:23;15988:18;16029:2;16021:6;16018:14;16015:2;;;16050:6;16042;16035:22;16015:2;16094:76;16162:7;16153:6;16142:9;16138:22;16094:76;:::i;:::-;16189:8;;-1:-1:-1;16068:102:1;-1:-1:-1;16277:2:1;16262:18;;16249:32;;-1:-1:-1;16293:16:1;;;16290:2;;;16327:6;16319;16312:22;16290:2;;16371:78;16441:7;16430:8;16419:9;16415:24;16371:78;:::i;:::-;15851:658;;;;-1:-1:-1;16468:8:1;-1:-1:-1;;;;15851:658:1:o;17334:1383::-;;;;;;17651:3;17639:9;17630:7;17626:23;17622:33;17619:2;;;17673:6;17665;17658:22;17619:2;17718:9;17705:23;17747:18;17788:2;17780:6;17777:14;17774:2;;;17809:6;17801;17794:22;17774:2;17837:67;17896:7;17887:6;17876:9;17872:22;17837:67;:::i;:::-;17827:77;;17957:2;17946:9;17942:18;17929:32;17913:48;;17986:2;17976:8;17973:16;17970:2;;;18007:6;17999;17992:22;17970:2;18035:69;18096:7;18085:8;18074:9;18070:24;18035:69;:::i;:::-;18025:79;;18157:2;18146:9;18142:18;18129:32;18113:48;;18186:2;18176:8;18173:16;18170:2;;;18207:6;18199;18192:22;18170:2;18235:75;18302:7;18291:8;18280:9;18276:24;18235:75;:::i;:::-;18225:85;;18363:2;18352:9;18348:18;18335:32;18319:48;;18392:2;18382:8;18379:16;18376:2;;;18413:6;18405;18398:22;18376:2;18441:69;18502:7;18491:8;18480:9;18476:24;18441:69;:::i;:::-;18431:79;;18563:3;18552:9;18548:19;18535:33;18519:49;;18593:2;18583:8;18580:16;18577:2;;;18614:6;18606;18599:22;18577:2;;18642:69;18703:7;18692:8;18681:9;18677:24;18642:69;:::i;18722:488::-;;;18894:2;18882:9;18873:7;18869:23;18865:32;18862:2;;;18915:6;18907;18900:22;18862:2;18960:9;18947:23;18993:18;18985:6;18982:30;18979:2;;;19030:6;19022;19015:22;18979:2;19074:76;19142:7;19133:6;19122:9;19118:22;19074:76;:::i;:::-;19169:8;;19048:102;;-1:-1:-1;18852:358:1;-1:-1:-1;;;;18852:358:1:o;19215:253::-;;19324:2;19312:9;19303:7;19299:23;19295:32;19292:2;;;19345:6;19337;19330:22;19292:2;19389:9;19376:23;19408:30;19432:5;19408:30;:::i;19473:257::-;;19593:2;19581:9;19572:7;19568:23;19564:32;19561:2;;;19614:6;19606;19599:22;19561:2;19651:9;19645:16;19670:30;19694:5;19670:30;:::i;19735:209::-;;19878:4;19866:9;19857:7;19853:23;19849:34;19846:2;;;19901:6;19893;19886:22;19846:2;-1:-1:-1;19929:9:1;19836:108;-1:-1:-1;19836:108:1:o;19949:257::-;;20060:2;20048:9;20039:7;20035:23;20031:32;20028:2;;;20081:6;20073;20066:22;20028:2;20125:9;20112:23;20144:32;20170:5;20144:32;:::i;20211:261::-;;20333:2;20321:9;20312:7;20308:23;20304:32;20301:2;;;20354:6;20346;20339:22;20301:2;20391:9;20385:16;20410:32;20436:5;20410:32;:::i;20477:190::-;;20589:2;20577:9;20568:7;20564:23;20560:32;20557:2;;;20610:6;20602;20595:22;20557:2;-1:-1:-1;20638:23:1;;20547:120;-1:-1:-1;20547:120:1:o;20672:194::-;;20795:2;20783:9;20774:7;20770:23;20766:32;20763:2;;;20816:6;20808;20801:22;20763:2;-1:-1:-1;20844:16:1;;20753:113;-1:-1:-1;20753:113:1:o;20871:257::-;;20982:2;20970:9;20961:7;20957:23;20953:32;20950:2;;;21003:6;20995;20988:22;20950:2;21047:9;21034:23;21066:32;21092:5;21066:32;:::i;21133:261::-;;21255:2;21243:9;21234:7;21230:23;21226:32;21223:2;;;21276:6;21268;21261:22;21223:2;21313:9;21307:16;21332:32;21358:5;21332:32;:::i;21399:400::-;;;21527:2;21515:9;21506:7;21502:23;21498:32;21495:2;;;21548:6;21540;21533:22;21495:2;21592:9;21579:23;21611:32;21637:5;21611:32;:::i;21804:398::-;;;21931:2;21919:9;21910:7;21906:23;21902:32;21899:2;;;21952:6;21944;21937:22;21899:2;21996:9;21983:23;22015:32;22041:5;22015:32;:::i;:::-;22066:5;-1:-1:-1;22123:2:1;22108:18;;22095:32;22136:34;22095:32;22136:34;:::i;22207:194::-;;22317:2;22305:9;22296:7;22292:23;22288:32;22285:2;;;22338:6;22330;22323:22;22285:2;22366:29;22385:9;22366:29;:::i;22406:261::-;;22528:2;22516:9;22507:7;22503:23;22499:32;22496:2;;;22549:6;22541;22534:22;22496:2;22586:9;22580:16;22605:32;22631:5;22605:32;:::i;22672:555::-;;22778:6;22773:3;22766:19;22804:4;22833:2;22828:3;22824:12;22817:19;;22859:5;22882:3;22894:308;22908:6;22905:1;22902:13;22894:308;;;22985:6;22972:20;23005:35;23032:7;23005:35;:::i;:::-;23078:42;23065:56;23053:69;;23142:12;;;;23177:15;;;;22930:1;22923:9;22894:308;;;-1:-1:-1;23218:3:1;;22756:471;-1:-1:-1;;;;;22756:471:1:o;23232:492::-;;23329:5;23323:12;23356:6;23351:3;23344:19;23382:4;23411:2;23406:3;23402:12;23395:19;;23448:2;23441:5;23437:14;23469:3;23481:218;23495:6;23492:1;23489:13;23481:218;;;23560:13;;23575:42;23556:62;23544:75;;23639:12;;;;23674:15;;;;23517:1;23510:9;23481:218;;23729:165;23813:6;23853:2;23846:5;23841:3;23828:28;23886:1;23881:2;23876:3;23872:12;23865:23;;23793:101;;:::o;23899:169::-;23987:6;24027:2;24020:5;24015:3;24002:28;24060:1;24055:2;24050:3;24046:12;24039:23;;23967:101;;:::o;24073:195::-;24187:6;24227:2;24220:5;24215:3;24202:28;24260:1;24255:2;24250:3;24246:12;24239:23;;24167:101;;:::o;24273:443::-;;24370:5;24364:12;24397:6;24392:3;24385:19;24423:4;24452:2;24447:3;24443:12;24436:19;;24489:2;24482:5;24478:14;24510:3;24522:169;24536:6;24533:1;24530:13;24522:169;;;24597:13;;24585:26;;24631:12;;;;24666:15;;;;24558:1;24551:9;24522:169;;24721:93;24793:13;24786:21;24774:34;;24764:50::o;24819:329::-;;24909:6;24904:3;24897:19;24961:6;24954:5;24947:4;24942:3;24938:14;24925:43;25013:3;25006:4;24997:6;24992:3;24988:16;24984:27;24977:40;25137:4;25067:66;25062:2;25054:6;25050:15;25046:88;25041:3;25037:98;25033:109;25026:116;;24887:261;;;;;:::o;25153:318::-;;25234:5;25228:12;25261:6;25256:3;25249:19;25277:63;25333:6;25326:4;25321:3;25317:14;25310:4;25303:5;25299:16;25277:63;:::i;:::-;25385:2;25373:15;25390:66;25369:88;25360:98;;;;25460:4;25356:109;;25204:267;-1:-1:-1;;25204:267:1:o;25476:132::-;25558:1;25551:5;25548:12;25538:2;;25564:9;25538:2;25584:18;;25528:80::o;25613:498::-;25718:5;25705:19;25733:34;25759:7;25733:34;:::i;:::-;25801:6;25788:20;25776:33;;25857:4;25846:16;;25833:30;25872:34;25833:30;25872:34;:::i;:::-;25951:26;25938:40;25931:4;25922:14;;25915:64;26035:4;26024:16;;;26011:30;25995:14;;;25988:54;26098:4;26087:16;;;26074:30;26058:14;;26051:54;25680:431::o;26116:670::-;26213:5;26200:19;26228:35;26255:7;26228:35;:::i;:::-;26282:42;26358:2;26349:7;26345:16;26340:3;26333:29;26386:44;26424:4;26417:5;26413:16;26386:44;:::i;:::-;26371:59;;26439:51;26484:4;26479:3;26475:14;26466:7;26439:51;:::i;:::-;26538:4;26531:5;26527:16;26514:30;26499:45;;26553:35;26580:7;26553:35;:::i;:::-;26620:16;26613:4;26604:14;;26597:40;26693:4;26682:16;;;26669:30;26653:14;;;26646:54;26774:4;26736:36;26766:4;26755:16;;26736:36;:::i;:::-;26732:47;26725:4;26720:3;26716:14;26709:71;26175:611;;:::o;26791:92::-;26869:6;26858:18;26846:31;;26836:47::o;26888:96::-;26966:10;26955:22;26943:35;;26933:51::o;26989:77::-;27058:4;27047:16;27035:29;;27033:33::o;27071:274::-;;27238:6;27232:13;27254:53;27300:6;27295:3;27288:4;27280:6;27276:17;27254:53;:::i;:::-;27323:16;;;;;27208:137;-1:-1:-1;;27208:137:1:o;27560:226::-;27736:42;27724:55;;;;27706:74;;27694:2;27679:18;;27661:125::o;27791:335::-;27983:42;28052:15;;;28034:34;;28104:15;;28099:2;28084:18;;28077:43;27961:2;27946:18;;27928:198::o;28131:593::-;28396:42;28465:15;;;28447:34;;28517:15;;;28512:2;28497:18;;28490:43;28569:15;;;;28564:2;28549:18;;28542:43;28633:6;28621:19;;;28616:2;28601:18;;28594:47;28690:26;28678:39;;;28672:3;28657:19;;28650:68;28373:3;28358:19;;28340:384::o;28729:479::-;28970:42;29039:15;;;29021:34;;29091:15;;;29086:2;29071:18;;29064:43;29143:15;;29138:2;29123:18;;29116:43;29190:2;29175:18;;29168:34;;;;28947:3;28932:19;;28914:294::o;29213:544::-;;29434:42;29515:2;29507:6;29503:15;29492:9;29485:34;29567:2;29559:6;29555:15;29550:2;29539:9;29535:18;29528:43;;29619:26;29611:6;29607:39;29602:2;29591:9;29587:18;29580:67;29683:3;29678:2;29667:9;29663:18;29656:31;29704:47;29746:3;29735:9;29731:19;29723:6;29704:47;:::i;:::-;29696:55;29414:343;-1:-1:-1;;;;;;29414:343:1:o;29762:297::-;29966:42;29954:55;;;;29936:74;;30041:2;30026:18;;30019:34;29924:2;29909:18;;29891:168::o;30366:869::-;30602:2;30614:21;;;30587:18;;30670:22;;;30366:869;30749:6;30723:2;30708:18;;30366:869;30786:329;30800:6;30797:1;30794:13;30786:329;;;30875:6;30862:20;30895:33;30922:5;30895:33;:::i;:::-;30964:42;30953:54;30941:67;;31031:4;31090:15;;;;31055:12;;;;30822:1;30815:9;30786:329;;;30790:3;31132;31124:11;;;;31185:42;31177:6;31173:55;31166:4;31155:9;31151:20;31144:85;30563:672;;;;;;:::o;31240:187::-;31405:14;;31398:22;31380:41;;31368:2;31353:18;;31335:92::o;31432:177::-;31578:25;;;31566:2;31551:18;;31533:76::o;31614:512::-;31873:25;;;31929:2;31914:18;;31907:34;;;;31972:2;31957:18;;31950:34;;;;32015:2;32000:18;;31993:34;32076:42;32064:55;32058:3;32043:19;;32036:84;31860:3;31845:19;;31827:299::o;32131:248::-;32305:66;32293:79;;;;32275:98;;32263:2;32248:18;;32230:149::o;32893:221::-;;33042:2;33031:9;33024:21;33062:46;33104:2;33093:9;33089:18;33081:6;33062:46;:::i;33119:335::-;33321:2;33303:21;;;33360:2;33340:18;;;33333:30;33399:13;33394:2;33379:18;;33372:41;33445:2;33430:18;;33293:161::o;33459:339::-;33661:2;33643:21;;;33700:2;33680:18;;;33673:30;33739:17;33734:2;33719:18;;33712:45;33789:2;33774:18;;33633:165::o;33803:334::-;34005:2;33987:21;;;34044:2;34024:18;;;34017:30;34083:12;34078:2;34063:18;;34056:40;34128:2;34113:18;;33977:160::o;34142:336::-;34344:2;34326:21;;;34383:2;34363:18;;;34356:30;34422:14;34417:2;34402:18;;34395:42;34469:2;34454:18;;34316:162::o;34483:335::-;34685:2;34667:21;;;34724:2;34704:18;;;34697:30;34763:13;34758:2;34743:18;;34736:41;34809:2;34794:18;;34657:161::o;34823:337::-;35025:2;35007:21;;;35064:2;35044:18;;;35037:30;35103:15;35098:2;35083:18;;35076:43;35151:2;35136:18;;34997:163::o;35165:334::-;35367:2;35349:21;;;35406:2;35386:18;;;35379:30;35445:12;35440:2;35425:18;;35418:40;35490:2;35475:18;;35339:160::o;35504:340::-;35706:2;35688:21;;;35745:2;35725:18;;;35718:30;35784:18;35779:2;35764:18;;35757:46;35835:2;35820:18;;35678:166::o;35849:335::-;36051:2;36033:21;;;36090:2;36070:18;;;36063:30;36129:13;36124:2;36109:18;;36102:41;36175:2;36160:18;;36023:161::o;36189:336::-;36391:2;36373:21;;;36430:2;36410:18;;;36403:30;36469:14;36464:2;36449:18;;36442:42;36516:2;36501:18;;36363:162::o;36530:336::-;36732:2;36714:21;;;36771:2;36751:18;;;36744:30;36810:14;36805:2;36790:18;;36783:42;36857:2;36842:18;;36704:162::o;36871:340::-;37073:2;37055:21;;;37112:2;37092:18;;;37085:30;37151:18;37146:2;37131:18;;37124:46;37202:2;37187:18;;37045:166::o;37216:339::-;37418:2;37400:21;;;37457:2;37437:18;;;37430:30;37496:17;37491:2;37476:18;;37469:45;37546:2;37531:18;;37390:165::o;37560:336::-;37762:2;37744:21;;;37801:2;37781:18;;;37774:30;37840:14;37835:2;37820:18;;37813:42;37887:2;37872:18;;37734:162::o;37901:346::-;38103:2;38085:21;;;38142:2;38122:18;;;38115:30;38181:24;38176:2;38161:18;;38154:52;38238:2;38223:18;;38075:172::o;38252:397::-;38454:2;38436:21;;;38493:2;38473:18;;;38466:30;38532:34;38527:2;38512:18;;38505:62;38603:3;38598:2;38583:18;;38576:31;38639:3;38624:19;;38426:223::o;38654:337::-;38856:2;38838:21;;;38895:2;38875:18;;;38868:30;38934:15;38929:2;38914:18;;38907:43;38982:2;38967:18;;38828:163::o;38996:336::-;39198:2;39180:21;;;39237:2;39217:18;;;39210:30;39276:14;39271:2;39256:18;;39249:42;39323:2;39308:18;;39170:162::o;39337:391::-;39559:13;;39574:10;39555:30;39537:49;;39646:4;39634:17;;;39628:24;39654:66;39624:97;39602:20;;;39595:127;;;;39525:2;39510:18;;39492:236::o;39733:848::-;;39921:3;39910:9;39906:19;39898:27;;39958:6;39952:13;39941:9;39934:32;40022:4;40014:6;40010:17;40004:24;39997:4;39986:9;39982:20;39975:54;40085:4;40077:6;40073:17;40067:24;40060:4;40049:9;40045:20;40038:54;40148:4;40140:6;40136:17;40130:24;40123:4;40112:9;40108:20;40101:54;40211:4;40203:6;40199:17;40193:24;40186:4;40175:9;40171:20;40164:54;40274:4;40266:6;40262:17;40256:24;40249:4;40238:9;40234:20;40227:54;40337:4;40329:6;40325:17;40319:24;40312:4;40301:9;40297:20;40290:54;40400:4;40392:6;40388:17;40382:24;40375:4;40364:9;40360:20;40353:54;40426:6;40486:2;40478:6;40474:15;40468:22;40463:2;40452:9;40448:18;40441:50;;40510:6;40570:2;40562:6;40558:15;40552:22;40547:2;40536:9;40532:18;40525:50;;39888:693;;;;:::o;40586:329::-;40792:25;;;40865:42;40853:55;40848:2;40833:18;;40826:83;40780:2;40765:18;;40747:168::o;40920:506::-;41187:25;;;41260:42;41248:55;;;;41243:2;41228:18;;41221:83;41352:6;41340:19;41335:2;41320:18;;41313:47;41408:10;41396:23;41391:2;41376:18;;41369:51;41174:3;41159:19;;41141:285::o;41431:430::-;41665:25;;;41709:42;41787:15;;;41782:2;41767:18;;41760:43;41839:15;41834:2;41819:18;;41812:43;41653:2;41638:18;;41620:241::o;41866:950::-;;42251:3;42281:6;42270:9;42263:25;42307:42;42397:2;42389:6;42385:15;42380:2;42369:9;42365:18;42358:43;42449:2;42441:6;42437:15;42432:2;42421:9;42417:18;42410:43;42462:54;42512:2;42501:9;42497:18;42489:6;42462:54;:::i;:::-;42565:2;42557:6;42553:15;42547:3;42536:9;42532:19;42525:44;;42606:6;42600:3;42589:9;42585:19;42578:35;42662:26;42654:6;42650:39;42644:3;42633:9;42629:19;42622:68;42727:2;42721:3;42710:9;42706:19;42699:31;42747:63;42806:2;42795:9;42791:18;42783:6;42775;42747:63;:::i;:::-;42739:71;42231:585;-1:-1:-1;;;;;;;;;;;;42231:585:1:o;42821:604::-;43122:25;;;43166:42;43244:15;;;43239:2;43224:18;;43217:43;43296:15;;43291:2;43276:18;;43269:43;43109:3;43094:19;;43321:54;43371:2;43356:18;;43348:6;43321:54;:::i;:::-;43412:6;43406:3;43395:9;43391:19;43384:35;43076:349;;;;;;;;:::o;43430:472::-;43693:25;;;43766:42;43754:55;;;;43749:2;43734:18;;43727:83;43841:2;43826:18;;43819:34;43884:2;43869:18;;43862:34;43680:3;43665:19;;43647:255::o;44410:634::-;;44747:6;44736:9;44729:25;44790:2;44785;44774:9;44770:18;44763:30;44816:79;44891:2;44880:9;44876:18;44868:6;44860;44816:79;:::i;:::-;44943:9;44935:6;44931:22;44926:2;44915:9;44911:18;44904:50;44971:67;45031:6;45023;45015;44971:67;:::i;:::-;44963:75;44719:325;-1:-1:-1;;;;;;;;44719:325:1:o;45049:1562::-;;45624:6;45613:9;45606:25;45650:2;45688:3;45683:2;45672:9;45668:18;45661:31;45715:63;45773:3;45762:9;45758:19;45750:6;45715:63;:::i;:::-;45826:9;45818:6;45814:22;45809:2;45798:9;45794:18;45787:50;45860;45903:6;45895;45860:50;:::i;:::-;45946:22;;;45941:2;45926:18;;45919:50;46018:13;;46040:22;;;46116:15;;;;-1:-1:-1;46078:15:1;;;;46149:4;46162:189;46176:6;46173:1;46170:13;46162:189;;;46225:46;46267:3;46258:6;46252:13;46225:46;:::i;:::-;46326:15;;;;46291:12;;;;46198:1;46191:9;46162:189;;;46166:3;;46397:9;46392:3;46388:19;46382:3;46371:9;46367:19;46360:48;46431:47;46474:3;46466:6;46431:47;:::i;:::-;46417:61;;;;46527:9;46519:6;46515:22;46509:3;46498:9;46494:19;46487:51;46555:50;46598:6;46590;46555:50;:::i;:::-;46547:58;45596:1015;-1:-1:-1;;;;;;;;;45596:1015:1:o;46616:2660::-;46957:25;;;46905:2;47001;47019:18;;;47012:30;;;46930:18;;;47077:22;;;46616:2660;;46905:2;47118;;47136:18;;;47196:15;;;47181:31;;47177:40;;47240:6;46616:2660;47277:1970;47291:6;47288:1;47285:13;47277:1970;;;47380:66;47368:9;47360:6;47356:22;47352:95;47347:3;47340:108;47500:6;47487:20;47587:66;47578:6;47562:14;47558:27;47554:100;47534:18;47530:125;47520:2;;47672:4;47666;47659:18;47520:2;47705:31;;47759:6;47778:53;47824:6;47797:25;47705:31;47797:25;:::i;:::-;47778:53;:::i;:::-;47859:35;47890:2;47883:5;47879:14;47859:35;:::i;:::-;47907:45;47948:2;47940:6;47936:15;47927:7;47907:45;:::i;:::-;;47980:34;48010:2;48003:5;47999:14;47980:34;:::i;:::-;48027:44;48067:2;48059:6;48055:15;48046:7;48027:44;:::i;:::-;;48118:55;48169:2;48162:5;48158:14;48151:5;48118:55;:::i;:::-;48210:2;48205;48197:6;48193:15;48186:27;48240:72;48308:2;48300:6;48296:15;48282:12;48268;48240:72;:::i;:::-;48226:86;;;;48335:4;48352:101;48449:2;48441:6;48437:15;48432:2;48425:5;48421:14;48352:101;:::i;:::-;;48476:6;48510:33;48539:2;48532:5;48528:14;48510:33;:::i;:::-;48556:43;48595:2;48587:6;48583:15;48574:7;48556:43;:::i;:::-;;;48622:6;48679:55;48730:2;48723:5;48719:14;48712:5;48679:55;:::i;:::-;48783:6;48775;48771:19;48766:2;48758:6;48754:15;48747:44;48818:67;48878:6;48862:14;48846;48818:67;:::i;:::-;48804:81;;;;;48908:6;48965:55;49016:2;49009:5;49005:14;48998:5;48965:55;:::i;:::-;48927:93;;49069:6;49061;49057:19;49052:2;49044:6;49040:15;49033:44;49100:67;49160:6;49144:14;49128;49100:67;:::i;:::-;49225:12;;;;49090:77;-1:-1:-1;;;49190:15:1;;;;-1:-1:-1;;47313:1:1;47306:9;47277:1970;;;-1:-1:-1;49264:6:1;;46885:2391;-1:-1:-1;;;;;;;;;;46885:2391:1:o;49281:1307::-;49549:25;;;49535:4;49520:20;;49596;;49625:32;49596:20;49625:32;:::i;:::-;49704:10;49697:5;49693:22;49688:2;49677:9;49673:18;49666:50;;49765:2;49757:6;49753:15;49740:29;49778:35;49805:7;49778:35;:::i;:::-;49862:42;49853:7;49849:56;49844:2;49833:9;49829:18;49822:84;;49967:2;49959:6;49955:15;49942:29;49937:2;49926:9;49922:18;49915:57;50034:2;50026:6;50022:15;50009:29;50003:3;49992:9;49988:19;49981:58;50063:37;50095:3;50087:6;50083:16;50063:37;:::i;:::-;50109:49;50153:3;50142:9;50138:19;50129:7;50109:49;:::i;:::-;;50220:3;50212:6;50208:16;50195:30;50189:3;50178:9;50174:19;50167:59;50235:80;50310:3;50299:9;50295:19;50289:3;50281:6;50277:16;50235:80;:::i;:::-;50324:75;50394:3;50383:9;50379:19;50370:6;50362;50358:19;50324:75;:::i;:::-;50408:84;50487:3;50476:9;50472:19;50463:6;50455;50451:19;50408:84;:::i;:::-;50501:81;50576:4;50565:9;50561:20;50552:6;50544;50540:19;50501:81;:::i;50593:291::-;50797:25;;;50870:6;50858:19;50853:2;50838:18;;50831:47;50785:2;50770:18;;50752:132::o;50889:295::-;51093:25;;;51166:10;51154:23;51149:2;51134:18;;51127:51;51081:2;51066:18;;51048:136::o;51189:188::-;51363:6;51351:19;;;;51333:38;;51321:2;51306:18;;51288:89::o;51564:192::-;51738:10;51726:23;;;;51708:42;;51696:2;51681:18;;51663:93::o;51761:506::-;52032:10;52020:23;;;;52002:42;;52092:4;52080:17;;;52075:2;52060:18;;52053:45;52134:17;;;52129:2;52114:18;;52107:45;52188:17;;52183:2;52168:18;;52161:45;52243:17;52237:3;52222:19;;52215:46;51989:3;51974:19;;51956:311::o;52272:184::-;52444:4;52432:17;;;;52414:36;;52402:2;52387:18;;52369:87::o;52461:208::-;52635:26;52623:39;;;;52605:58;;52593:2;52578:18;;52560:109::o;52674:242::-;52744:2;52738:9;52774:17;;;52821:18;52806:34;;52842:22;;;52803:62;52800:2;;;52868:9;52800:2;52895;52888:22;52718:198;;-1:-1:-1;52718:198:1:o;52921:183::-;;53020:18;53012:6;53009:30;53006:2;;;53042:9;53006:2;-1:-1:-1;53093:4:1;53074:17;;;53070:28;;52996:108::o;53109:572::-;;;53236:3;53223:17;53318:66;53307:8;53291:14;53287:29;53283:102;53263:18;53259:127;53249:2;;53404:5;53397;53390:20;53249:2;53436:33;;53540:4;53527:18;;;-1:-1:-1;53488:21:1;;-1:-1:-1;53568:18:1;53557:30;;53554:2;;;53600:1;53597;53590:12;53554:2;53650:6;53634:14;53630:27;53620:8;53616:42;53613:2;;;53671:1;53668;53661:12;53686:258;53758:1;53768:113;53782:6;53779:1;53776:13;53768:113;;;53858:11;;;53852:18;53839:11;;;53832:39;53804:2;53797:10;53768:113;;;53899:6;53896:1;53893:13;53890:2;;;-1:-1:-1;;53934:1:1;53916:16;;53909:27;53739:205::o;53949:156::-;54037:42;54030:5;54026:54;54019:5;54016:65;54006:2;;54095:1;54092;54085:12;54006:2;53996:109;:::o;54110:120::-;54198:5;54191:13;54184:21;54177:5;54174:32;54164:2;;54220:1;54217;54210:12;54235:119;54322:6;54315:5;54311:18;54304:5;54301:29;54291:2;;54344:1;54341;54334:12;54359:123;54446:10;54439:5;54435:22;54428:5;54425:33;54415:2;;54472:1;54469;54462:12;54487:139;54574:26;54567:5;54563:38;54556:5;54553:49;54543:2;;54616:1;54613;54606:12
Swarm Source
ipfs://90e1d0b4b5978ee2e8ccae3378a2c50a98e4f9a781297a63e9886657ead8f536
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.