More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 13,208 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Execute Fast Wit... | 19692319 | 278 days ago | IN | 0 ETH | 0.00113262 | ||||
Execute Fast Wit... | 19692307 | 278 days ago | IN | 60 ETH | 0.0007293 | ||||
Execute Fast Wit... | 19692163 | 278 days ago | IN | 46 ETH | 0.00083188 | ||||
Execute Fast Wit... | 19690754 | 278 days ago | IN | 0.03678 ETH | 0.00154578 | ||||
Execute Fast Wit... | 19690560 | 278 days ago | IN | 0.00005809 ETH | 0.00173148 | ||||
Execute Fast Wit... | 19690024 | 278 days ago | IN | 0.31572295 ETH | 0.00156413 | ||||
Execute Fast Wit... | 19687710 | 279 days ago | IN | 0 ETH | 0.00114154 | ||||
Execute Fast Wit... | 19685834 | 279 days ago | IN | 0 ETH | 0.00082002 | ||||
Execute Fast Wit... | 19685055 | 279 days ago | IN | 0 ETH | 0.00112345 | ||||
Execute Fast Wit... | 19684628 | 279 days ago | IN | 0.038565 ETH | 0.00103261 | ||||
Execute Fast Wit... | 19683669 | 279 days ago | IN | 0 ETH | 0.00171927 | ||||
Execute Fast Wit... | 19683278 | 279 days ago | IN | 0.11834511 ETH | 0.00201577 | ||||
Execute Fast Wit... | 19681835 | 280 days ago | IN | 0.016826 ETH | 0.00207835 | ||||
Execute Fast Wit... | 19681835 | 280 days ago | IN | 0.0159367 ETH | 0.00215074 | ||||
Execute Fast Wit... | 19681831 | 280 days ago | IN | 0.0159367 ETH | 0.00230219 | ||||
Execute Fast Wit... | 19680499 | 280 days ago | IN | 0.03559137 ETH | 0.00114767 | ||||
Execute Fast Wit... | 19678791 | 280 days ago | IN | 11.17915509 ETH | 0.00103023 | ||||
Execute Fast Wit... | 19678087 | 280 days ago | IN | 0.03519795 ETH | 0.00108014 | ||||
Execute Fast Wit... | 19677028 | 280 days ago | IN | 0 ETH | 0.0027269 | ||||
Execute Fast Wit... | 19674637 | 281 days ago | IN | 0.10440541 ETH | 0.00117266 | ||||
Execute Fast Wit... | 19671220 | 281 days ago | IN | 0.02956911 ETH | 0.00072651 | ||||
Execute Fast Wit... | 19670788 | 281 days ago | IN | 0.05137515 ETH | 0.00117948 | ||||
Execute Fast Wit... | 19670629 | 281 days ago | IN | 0.0092575 ETH | 0.001145 | ||||
Execute Fast Wit... | 19669244 | 281 days ago | IN | 0.0155456 ETH | 0.00310174 | ||||
Execute Fast Wit... | 19664450 | 282 days ago | IN | 2.36 ETH | 0.0008291 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
19692307 | 278 days ago | 60 ETH | ||||
19692163 | 278 days ago | 46 ETH | ||||
19690754 | 278 days ago | 0.03678 ETH | ||||
19690560 | 278 days ago | 0.00005809 ETH | ||||
19690024 | 278 days ago | 0.31572295 ETH | ||||
19684628 | 279 days ago | 0.038565 ETH | ||||
19683278 | 279 days ago | 0.11834511 ETH | ||||
19681835 | 280 days ago | 0.016826 ETH | ||||
19681835 | 280 days ago | 0.0159367 ETH | ||||
19681831 | 280 days ago | 0.0159367 ETH | ||||
19680499 | 280 days ago | 0.03559137 ETH | ||||
19678791 | 280 days ago | 11.17915509 ETH | ||||
19678087 | 280 days ago | 0.03519795 ETH | ||||
19674637 | 281 days ago | 0.10440541 ETH | ||||
19671220 | 281 days ago | 0.02956911 ETH | ||||
19670788 | 281 days ago | 0.05137515 ETH | ||||
19670629 | 281 days ago | 0.0092575 ETH | ||||
19669244 | 281 days ago | 0.0155456 ETH | ||||
19664450 | 282 days ago | 2.36 ETH | ||||
19664058 | 282 days ago | 0.17321774 ETH | ||||
19662244 | 282 days ago | 0.0033997 ETH | ||||
19657764 | 283 days ago | 0.15027045 ETH | ||||
19657381 | 283 days ago | 0.069 ETH | ||||
19654650 | 283 days ago | 0.09523126 ETH | ||||
19651335 | 284 days ago | 0.019 ETH |
Loading...
Loading
Contract Name:
FastWithdrawalAgent
Compiler Version
v0.7.0+commit.9e61f92b
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2020-10-29 */ // SPDX-License-Identifier: Apache-2.0 // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; interface IAgent{} interface 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 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 view returns (bool); } // 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); } } // 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); } } // 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); } // 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"); } } // Copyright 2017 Loopring Technology Limited. pragma experimental ABIEncoderV2; //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 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]; } } // 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)) } } } } /// @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 ); } } // 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 domainHash, bytes32 dataHash ) internal pure returns (bytes32) { return keccak256( abi.encodePacked( EIP191_HEADER, domainHash, dataHash ) ); } } // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. // 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. /// This will stop NEW blocks from using the given circuit, blocks that were already committed /// can still be verified. /// @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); } // 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); } // 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; address public lrcAddress; 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 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 ); } /// @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 } // -- 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; 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. AuxiliaryData[] 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; } function SNARK_SCALAR_FIELD() internal pure returns (uint) { // This is the prime number that is used for the alt_bn128 elliptic curve, see EIP-196. return 21888242871839275222246405745257275088548364400416034343698204186575808495617; } function MAX_OPEN_FORCED_REQUESTS() internal pure returns (uint16) { return 4096; } function MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE() internal pure returns (uint32) { return 15 days; } function TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS() internal pure returns (uint32) { return 7 days; } function MAX_NUM_ACCOUNTS() internal pure returns (uint) { return 2 ** 32; } function MAX_NUM_TOKENS() internal pure returns (uint) { return 2 ** 16; } function MIN_AGE_PROTOCOL_FEES_UNTIL_UPDATED() internal pure returns (uint32) { return 7 days; } function MIN_TIME_IN_SHUTDOWN() internal pure returns (uint32) { return 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. function TX_DATA_AVAILABILITY_SIZE() internal pure returns (uint32) { return 68; } function MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE_UPPERBOUND() internal pure returns (uint32) { return 15 days; } function ACCOUNTID_PROTOCOLFEE() internal pure returns (uint32) { return 0; } function TX_DATA_AVAILABILITY_SIZE_PART_1() internal pure returns (uint32) { return 29; } function TX_DATA_AVAILABILITY_SIZE_PART_2() internal pure returns (uint32) { return 39; } struct AccountLeaf { uint32 accountID; address owner; uint pubKeyX; uint pubKeyY; uint32 nonce; uint feeBipsAMM; } struct BalanceLeaf { uint16 tokenID; uint96 balance; uint96 weightAMM; uint storageRoot; } struct MerkleProof { ExchangeData.AccountLeaf accountLeaf; ExchangeData.BalanceLeaf balanceLeaf; uint[48] accountMerkleProof; uint[24] balanceMerkleProof; } struct BlockContext { bytes32 DOMAIN_SEPARATOR; uint32 timestamp; } // 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) 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; } } /// @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 ForcedWithdrawalRequested( address owner, address token, 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 ProtocolFeesUpdated( uint8 takerFeeBips, uint8 makerFeeBips, uint8 previousTakerFeeBips, uint8 previousMakerFeeBips ); event TransactionApproved( address owner, bytes32 transactionHash ); // events from libraries /*event DepositProcessed( address to, uint32 toAccountId, uint16 token, uint amount );*/ /*event ForcedWithdrawalProcessed( uint32 fromAccountID, uint16 tokenID, uint amount );*/ /*event ConditionalTransferProcessed( address from, address to, uint16 token, uint amount );*/ /*event AccountUpdated( uint32 owner, uint publicKey );*/ // -- 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 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'. /// /// A fee to the owner is paid in ETH to process the deposit. /// The operator is not forced to do the deposit and the user can send /// any fee amount. /// /// @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 auxiliaryData Optional extra data used by the deposit contract function deposit( address from, address to, address tokenAddress, uint96 amount, bytes calldata auxiliaryData ) external virtual payable; /// @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 ) external virtual view returns (uint96); // -- Withdrawals -- /// @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 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 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 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 token The token address of the the forced request function notifyForcedRequestTooOld( uint32 accountID, address token ) 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); } // 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; } } // 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; } } /// @title Fast withdrawal agent implementation. With the help of liquidity providers (LPs), /// exchange operators can convert any normal withdrawals into fast withdrawals. /// /// Fast withdrawals are a way for the owner to provide instant withdrawals for /// users with the help of a liquidity provider and conditional transfers. /// /// A fast withdrawal requires the non-trustless cooperation of 2 parties: /// - A liquidity provider which provides funds to users immediately onchain /// - The operator which will make sure the user has sufficient funds offchain /// so that the liquidity provider can be paid back. /// The operator also needs to process those withdrawals so that the /// liquidity provider receives its funds back. /// /// We require the fast withdrawals to be executed by the liquidity provider (as msg.sender) /// so that the liquidity provider can impose its own rules on how its funds are spent. This will /// inevitably need to be done in close cooperation with the operator, or by the operator /// itself using a smart contract where the liquidity provider enforces who, how /// and even if their funds can be used to facilitate the fast withdrawals. /// /// The liquidity provider can call `executeFastWithdrawals` to provide users /// immediately with funds onchain. This allows the security of the funds to be handled /// by any EOA or smart contract. /// /// Users that want to make use of this functionality have to /// authorize this contract as their agent. /// /// @author Brecht Devos - <[email protected]> /// @author Kongliang Zhong - <[email protected]> /// @author Daniel Wang - <[email protected]> contract FastWithdrawalAgent is ReentrancyGuard, IAgent { using AddressUtil for address; using AddressUtil for address payable; using ERC20SafeTransfer for address; using MathUint for uint; event Processed( address exchange, address from, address to, address token, uint96 amount, address provider, bool success ); struct Withdrawal { address exchange; address from; // The owner of the account address to; // The `to` address of the withdrawal address token; uint96 amount; uint32 storageID; } // This method needs to be called by any liquidity provider function executeFastWithdrawals(Withdrawal[] calldata withdrawals) public nonReentrant payable { // Do all fast withdrawals for (uint i = 0; i < withdrawals.length; i++) { executeInternal(withdrawals[i]); } // Return any ETH left into this contract // (can happen when more ETH is sent than needed for the fast withdrawals) msg.sender.sendETHAndVerify(address(this).balance, gasleft()); } // -- Internal -- function executeInternal(Withdrawal calldata withdrawal) internal { require( withdrawal.exchange != address(0) && withdrawal.from != address(0) && withdrawal.to != address(0) && withdrawal.amount != 0, "INVALID_WITHDRAWAL" ); // The liquidity provider always authorizes the fast withdrawal by being the direct caller address payable liquidityProvider = msg.sender; bool success; // Override the destination address of a withdrawal to the address of the liquidity provider try IExchangeV3(withdrawal.exchange).setWithdrawalRecipient( withdrawal.from, withdrawal.to, withdrawal.token, withdrawal.amount, withdrawal.storageID, liquidityProvider ) { // Transfer the tokens immediately to the requested address // using funds from the liquidity provider (`msg.sender`). transfer( liquidityProvider, withdrawal.to, withdrawal.token, withdrawal.amount ); success = true; } catch { success = false; } emit Processed( withdrawal.exchange, withdrawal.from, withdrawal.to, withdrawal.token, withdrawal.amount, liquidityProvider, success ); } function transfer( address from, address to, address token, uint amount ) internal { if (amount > 0) { if (token == address(0)) { to.sendETHAndVerify(amount, gasleft()); // ETH } else { token.safeTransferFromAndVerify(from, to, amount); // ERC20 token } } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"exchange","type":"address"},{"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":"uint96","name":"amount","type":"uint96"},{"indexed":false,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"Processed","type":"event"},{"inputs":[{"components":[{"internalType":"address","name":"exchange","type":"address"},{"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":"struct FastWithdrawalAgent.Withdrawal[]","name":"withdrawals","type":"tuple[]"}],"name":"executeFastWithdrawals","outputs":[],"stateMutability":"payable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50610964806100206000396000f3fe60806040526004361061001e5760003560e01c8063db430b3a14610023575b600080fd5b6100366100313660046106ab565b610038565b005b6000541561007b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161007290610889565b60405180910390fd5b600160009081555b818110156100ad576100a583838381811061009a57fe5b905060c002016100c4565b600101610083565b506100bb475a33919061035c565b50506000805550565b60006100d36020830183610677565b73ffffffffffffffffffffffffffffffffffffffff161415801561011d575060006101046040830160208401610677565b73ffffffffffffffffffffffffffffffffffffffff1614155b801561014f575060006101366060830160408401610677565b73ffffffffffffffffffffffffffffffffffffffff1614155b8015610177575061016660a082016080830161073f565b6bffffffffffffffffffffffff1615155b6101ad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610072906108c0565b3360006101bd6020840184610677565b73ffffffffffffffffffffffffffffffffffffffff1663d5b039ce6101e86040860160208701610677565b6101f86060870160408801610677565b6102086080880160608901610677565b61021860a0890160808a0161073f565b61022860c08a0160a08b0161071b565b886040518763ffffffff1660e01b815260040161024a969594939291906107ff565b600060405180830381600087803b15801561026457600080fd5b505af1925050508015610275575060015b610281575060006102cc565b6102c8826102956060860160408701610677565b6102a56080870160608801610677565b6102b560a088016080890161073f565b6bffffffffffffffffffffffff166103bf565b5060015b7f8e78d5f76bce51536c826665d9ca105f063f3df143c73412da3d86fb0ebf3fac6102fa6020850185610677565b61030a6040860160208701610677565b61031a6060870160408801610677565b61032a6080880160608901610677565b61033a60a0890160808a0161073f565b878760405161034f97969594939291906107a4565b60405180910390a1505050565b600061037f73ffffffffffffffffffffffffffffffffffffffff85168484610431565b9050806103b8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610072906108f7565b9392505050565b801561042b5773ffffffffffffffffffffffffffffffffffffffff821661040957610403815a73ffffffffffffffffffffffffffffffffffffffff8616919061035c565b5061042b565b61042b73ffffffffffffffffffffffffffffffffffffffff83168585846104d8565b50505050565b600082610440575060016103b8565b60006104618573ffffffffffffffffffffffffffffffffffffffff166104e5565b90508073ffffffffffffffffffffffffffffffffffffffff16848490604051610489906104e5565b600060405180830381858888f193505050503d80600081146104c7576040519150601f19603f3d011682016040523d82523d6000602084013e6104cc565b606091505b50909695505050505050565b61042b848484845a6104e8565b90565b60006104f78686868686610538565b905080610530576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610072906108f7565b505050505050565b600060606323b872dd60e01b86868660405160240161055993929190610858565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008773ffffffffffffffffffffffffffffffffffffffff1684836040516105e0919061076b565b60006040518083038160008787f1925050503d806000811461061e576040519150601f19603f3d011682016040523d82523d6000602084013e610623565b606091505b505090506106308161063c565b98975050505050505050565b60008115610673573d801561065c57602081146106655760009250610671565b60019250610671565b60206000803e60005192505b505b5090565b600060208284031215610688578081fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146103b8578182fd5b600080602083850312156106bd578081fd5b823567ffffffffffffffff808211156106d4578283fd5b818501915085601f8301126106e7578283fd5b8135818111156106f5578384fd5b86602060c083028501011115610709578384fd5b60209290920196919550909350505050565b60006020828403121561072c578081fd5b813563ffffffff811681146103b8578182fd5b600060208284031215610750578081fd5b81356bffffffffffffffffffffffff811681146103b8578182fd5b60008251815b8181101561078b5760208186018101518583015201610771565b818111156107995782828501525b509190910192915050565b73ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616604086015291851660608501526bffffffffffffffffffffffff16608084015290921660a082015290151560c082015260e00190565b73ffffffffffffffffffffffffffffffffffffffff9687168152948616602086015292851660408501526bffffffffffffffffffffffff91909116606084015263ffffffff16608083015290911660a082015260c00190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6020808252600a908201527f5245454e5452414e435900000000000000000000000000000000000000000000604082015260600190565b60208082526012908201527f494e56414c49445f5749544844524157414c0000000000000000000000000000604082015260600190565b60208082526010908201527f5452414e534645525f4641494c5552450000000000000000000000000000000060408201526060019056fea26469706673582212200b80eb098e763c2c5a52d2c1fffc5c8990633ce5a21624f613adaed1266723a164736f6c63430007000033
Deployed Bytecode
0x60806040526004361061001e5760003560e01c8063db430b3a14610023575b600080fd5b6100366100313660046106ab565b610038565b005b6000541561007b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161007290610889565b60405180910390fd5b600160009081555b818110156100ad576100a583838381811061009a57fe5b905060c002016100c4565b600101610083565b506100bb475a33919061035c565b50506000805550565b60006100d36020830183610677565b73ffffffffffffffffffffffffffffffffffffffff161415801561011d575060006101046040830160208401610677565b73ffffffffffffffffffffffffffffffffffffffff1614155b801561014f575060006101366060830160408401610677565b73ffffffffffffffffffffffffffffffffffffffff1614155b8015610177575061016660a082016080830161073f565b6bffffffffffffffffffffffff1615155b6101ad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610072906108c0565b3360006101bd6020840184610677565b73ffffffffffffffffffffffffffffffffffffffff1663d5b039ce6101e86040860160208701610677565b6101f86060870160408801610677565b6102086080880160608901610677565b61021860a0890160808a0161073f565b61022860c08a0160a08b0161071b565b886040518763ffffffff1660e01b815260040161024a969594939291906107ff565b600060405180830381600087803b15801561026457600080fd5b505af1925050508015610275575060015b610281575060006102cc565b6102c8826102956060860160408701610677565b6102a56080870160608801610677565b6102b560a088016080890161073f565b6bffffffffffffffffffffffff166103bf565b5060015b7f8e78d5f76bce51536c826665d9ca105f063f3df143c73412da3d86fb0ebf3fac6102fa6020850185610677565b61030a6040860160208701610677565b61031a6060870160408801610677565b61032a6080880160608901610677565b61033a60a0890160808a0161073f565b878760405161034f97969594939291906107a4565b60405180910390a1505050565b600061037f73ffffffffffffffffffffffffffffffffffffffff85168484610431565b9050806103b8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610072906108f7565b9392505050565b801561042b5773ffffffffffffffffffffffffffffffffffffffff821661040957610403815a73ffffffffffffffffffffffffffffffffffffffff8616919061035c565b5061042b565b61042b73ffffffffffffffffffffffffffffffffffffffff83168585846104d8565b50505050565b600082610440575060016103b8565b60006104618573ffffffffffffffffffffffffffffffffffffffff166104e5565b90508073ffffffffffffffffffffffffffffffffffffffff16848490604051610489906104e5565b600060405180830381858888f193505050503d80600081146104c7576040519150601f19603f3d011682016040523d82523d6000602084013e6104cc565b606091505b50909695505050505050565b61042b848484845a6104e8565b90565b60006104f78686868686610538565b905080610530576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610072906108f7565b505050505050565b600060606323b872dd60e01b86868660405160240161055993929190610858565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008773ffffffffffffffffffffffffffffffffffffffff1684836040516105e0919061076b565b60006040518083038160008787f1925050503d806000811461061e576040519150601f19603f3d011682016040523d82523d6000602084013e610623565b606091505b505090506106308161063c565b98975050505050505050565b60008115610673573d801561065c57602081146106655760009250610671565b60019250610671565b60206000803e60005192505b505b5090565b600060208284031215610688578081fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146103b8578182fd5b600080602083850312156106bd578081fd5b823567ffffffffffffffff808211156106d4578283fd5b818501915085601f8301126106e7578283fd5b8135818111156106f5578384fd5b86602060c083028501011115610709578384fd5b60209290920196919550909350505050565b60006020828403121561072c578081fd5b813563ffffffff811681146103b8578182fd5b600060208284031215610750578081fd5b81356bffffffffffffffffffffffff811681146103b8578182fd5b60008251815b8181101561078b5760208186018101518583015201610771565b818111156107995782828501525b509190910192915050565b73ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616604086015291851660608501526bffffffffffffffffffffffff16608084015290921660a082015290151560c082015260e00190565b73ffffffffffffffffffffffffffffffffffffffff9687168152948616602086015292851660408501526bffffffffffffffffffffffff91909116606084015263ffffffff16608083015290911660a082015260c00190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6020808252600a908201527f5245454e5452414e435900000000000000000000000000000000000000000000604082015260600190565b60208082526012908201527f494e56414c49445f5749544844524157414c0000000000000000000000000000604082015260600190565b60208082526010908201527f5452414e534645525f4641494c5552450000000000000000000000000000000060408201526060019056fea26469706673582212200b80eb098e763c2c5a52d2c1fffc5c8990633ce5a21624f613adaed1266723a164736f6c63430007000033
Deployed Bytecode Sourcemap
76753:3294:0:-:0;;;;;;;;;;;;;;;;;;;;;77552:492;;;;;;:::i;:::-;;:::i;:::-;;;74714:11;;:16;74706:39;;;;;;;;;;;;:::i;:::-;;;;;;;;;74816:1;74802:11;:15;;;77726:104:::1;77743:22:::0;;::::1;77726:104;;;77787:31;77803:11;;77815:1;77803:14;;;;;;;;;;;;77787:15;:31::i;:::-;77767:3;;77726:104;;;;77975:61;78003:21;78026:9;77975:10;::::0;:61;:27:::1;:61::i;:::-;-1:-1:-1::0;;74915:1:0;74901:15;;-1:-1:-1;77552:492:0:o;78077:1543::-;78221:1;78190:19;;;;:10;:19;:::i;:::-;:33;;;;:79;;;;-1:-1:-1;78267:1:0;78240:15;;;;;;;;:::i;:::-;:29;;;;78190:79;:123;;;;-1:-1:-1;78311:1:0;78286:13;;;;;;;;:::i;:::-;:27;;;;78190:123;:162;;;;-1:-1:-1;78330:17:0;;;;;;;;:::i;:::-;:22;;;;78190:162;78168:230;;;;;;;;;;;;:::i;:::-;78547:10;78511:33;78711:19;;;;:10;:19;:::i;:::-;78699:55;;;78769:15;;;;;;;;:::i;:::-;78799:13;;;;;;;;:::i;:::-;78827:16;;;;;;;;:::i;:::-;78858:17;;;;;;;;:::i;:::-;78890:20;;;;;;;;:::i;:::-;78925:17;78699:254;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;78695:671;;-1:-1:-1;79349:5:0;78695:671;;;79114:162;79141:17;79177:13;;;;;;;;:::i;:::-;79209:16;;;;;;;;:::i;:::-;79244:17;;;;;;;;:::i;:::-;79114:162;;:8;:162::i;:::-;-1:-1:-1;79301:4:0;78695:671;79383:229;79407:19;;;;:10;:19;:::i;:::-;79441:15;;;;;;;;:::i;:::-;79471:13;;;;;;;;:::i;:::-;79499:16;;;;;;;;:::i;:::-;79530:17;;;;;;;;:::i;:::-;79562;79594:7;79383:229;;;;;;;;;;;;:::i;:::-;;;;;;;;78077:1543;;;:::o;16806:269::-;16952:12;16992:28;:10;;;17003:6;17011:8;16992:10;:28::i;:::-;16982:38;;17039:7;17031:36;;;;;;;;;;;;:::i;:::-;16806:269;;;;;:::o;79628:416::-;79788:10;;79784:253;;79819:19;;;79815:211;;79859:38;79879:6;79887:9;79859:19;;;;:38;:19;:38::i;:::-;;79815:211;;;79945:49;:31;;;79977:4;79983:2;79987:6;79945:31;:49::i;:::-;79628:416;;;;:::o;16256:395::-;16393:12;16427:11;16423:55;;-1:-1:-1;16462:4:0;16455:11;;16423:55;16488:25;16516:14;:2;:12;;;:14::i;:::-;16488:42;;16595:9;:14;;16617:6;16630:8;16595:48;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;16581:62:0;;16256:395;-1:-1:-1;;;;;;16256:395:0:o;70877:328::-;71049:148;71101:5;71121:4;71140:2;71157:5;71177:9;71049:37;:148::i;15941:164::-;16092:4;15941:164::o;71562:417::-;71773:11;71787:138;71830:5;71850:4;71869:2;71886:5;71906:8;71787:28;:138::i;:::-;71773:152;;71944:6;71936:35;;;;;;;;;;;;:::i;:::-;71562:417;;;;;;:::o;71987:862::-;72191:4;72587:21;72655:10;72648:18;;72681:4;72700:2;72717:5;72611:122;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;72587:146;;72745:12;72763:5;:10;;72779:8;72789;72763:35;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;72744:54;;;72816:25;72833:7;72816:16;:25::i;:::-;72809:32;71987:862;-1:-1:-1;;;;;;;;71987:862:0:o;72857:1127::-;72967:4;73290:7;73286:666;;;73349:16;73504:61;;;;73682:2;73677:115;;;;73906:1;73895:12;;73342:584;;73504:61;73545:1;73534:12;;73504:61;;73677:115;73729:2;73726:1;73723;73708:24;73771:1;73765:8;73754:19;;73342:584;;73323:618;-1:-1:-1;73969:7:0;72857:1127::o;850:241:-1:-;;954:2;942:9;933:7;929:23;925:32;922:2;;;-1:-1;;960:12;922:2;85:6;72:20;9221:42;10304:5;9210:54;10279:5;10276:35;10266:2;;-1:-1;;10315:12;1098:457;;;1267:2;1255:9;1246:7;1242:23;1238:32;1235:2;;;-1:-1;;1273:12;1235:2;1331:17;1318:31;1369:18;;1361:6;1358:30;1355:2;;;-1:-1;;1391:12;1355:2;1522:6;1511:9;1507:22;;;350:3;343:4;335:6;331:17;327:27;317:2;;-1:-1;;358:12;317:2;401:6;388:20;1369:18;420:6;417:30;414:2;;;-1:-1;;450:12;414:2;545:3;1267:2;537:4;529:6;525:17;486:6;511:32;;508:41;505:2;;;-1:-1;;552:12;505:2;1267;482:17;;;;;1411:128;;-1:-1;1229:326;;-1:-1;;;;1229:326::o;1562:239::-;;1665:2;1653:9;1644:7;1640:23;1636:32;1633:2;;;-1:-1;;1671:12;1633:2;659:6;646:20;9427:10;10426:5;9416:22;10402:5;10399:34;10389:2;;-1:-1;;10437:12;1808:239;;1911:2;1899:9;1890:7;1886:23;1882:32;1879:2;;;-1:-1;;1917:12;1879:2;794:6;781:20;9522:26;10548:5;9511:38;10524:5;10521:34;10511:2;;-1:-1;;10559:12;4427:271;;2594:5;8592:12;-1:-1;10013:101;10027:6;10024:1;10021:13;10013:101;;;2738:4;10094:11;;;;;10088:18;10075:11;;;10068:39;10042:10;10013:101;;;10129:6;10126:1;10123:13;10120:2;;;-1:-1;10185:6;10180:3;10176:16;10169:27;10120:2;-1:-1;2769:16;;;;;4561:137;-1:-1;;4561:137::o;5091:892::-;9221:42;9210:54;;;2274:37;;9210:54;;;5551:2;5536:18;;2274:37;9210:54;;;5634:2;5619:18;;2274:37;9210:54;;;5717:2;5702:18;;2274:37;9522:26;9511:38;5798:3;5783:19;;4379:36;9210:54;;;5890:3;5875:19;;2133:58;9122:13;;9115:21;5968:3;5953:19;;2388:34;5386:3;5371:19;;5357:626::o;5990:788::-;9221:42;9210:54;;;2274:37;;9210:54;;;6426:2;6411:18;;2274:37;9210:54;;;6509:2;6494:18;;2274:37;9522:26;9511:38;;;;6590:2;6575:18;;4379:36;9427:10;9416:22;6671:3;6656:19;;4262:36;9210:54;;;6763:3;6748:19;;2133:58;6261:3;6246:19;;6232:546::o;6785:444::-;9221:42;9210:54;;;2274:37;;9210:54;;;;7132:2;7117:18;;2274:37;7215:2;7200:18;;4144:37;;;;6968:2;6953:18;;6939:290::o;7236:416::-;7436:2;7450:47;;;3022:2;7421:18;;;8890:19;3058:12;8930:14;;;3038:33;3090:12;;;7407:245::o;7659:416::-;7859:2;7873:47;;;3341:2;7844:18;;;8890:19;3377:20;8930:14;;;3357:41;3417:12;;;7830:245::o;8082:416::-;8282:2;8296:47;;;3668:2;8267:18;;;8890:19;3704:18;8930:14;;;3684:39;3742:12;;;8253:245::o
Swarm Source
ipfs://0b80eb098e763c2c5a52d2c1fffc5c8990633ce5a21624f613adaed1266723a1
Loading...
Loading
Loading...
Loading
OVERVIEW
The Fast Withdrawal Agent for Loopring Exchange v2Loading...
Loading
[ Download: CSV Export ]
[ 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.