ETH Price: $2,308.20 (-4.46%)

Contract

0xec3Cc6Cf0252565b56FC7AC396017Df5b9B78a31
 
Transaction Hash
Method
Block
From
To
Execute Fast Wit...196923192024-04-19 22:00:23149 days ago1713564023IN
Loopring: Exchange v2 Fast Withdrawal
0 ETH0.001132629.35951631
Execute Fast Wit...196923072024-04-19 21:57:59149 days ago1713563879IN
Loopring: Exchange v2 Fast Withdrawal
60 ETH0.00072937.26576205
Execute Fast Wit...196921632024-04-19 21:29:11149 days ago1713562151IN
Loopring: Exchange v2 Fast Withdrawal
46 ETH0.000831888.28772959
Execute Fast Wit...196907542024-04-19 16:45:11149 days ago1713545111IN
Loopring: Exchange v2 Fast Withdrawal
0.03678 ETH0.0015457814.82738755
Execute Fast Wit...196905602024-04-19 16:05:47149 days ago1713542747IN
Loopring: Exchange v2 Fast Withdrawal
0.00005809 ETH0.0017314816.60720626
Execute Fast Wit...196900242024-04-19 14:17:23149 days ago1713536243IN
Loopring: Exchange v2 Fast Withdrawal
0.31572295 ETH0.0015641315.58276284
Execute Fast Wit...196877102024-04-19 6:29:47150 days ago1713508187IN
Loopring: Exchange v2 Fast Withdrawal
0 ETH0.001141548.47634674
Execute Fast Wit...196858342024-04-19 0:11:35150 days ago1713485495IN
Loopring: Exchange v2 Fast Withdrawal
0 ETH0.000820027.38790145
Execute Fast Wit...196850552024-04-18 21:34:59150 days ago1713476099IN
Loopring: Exchange v2 Fast Withdrawal
0 ETH0.001123459.28523501
Execute Fast Wit...196846282024-04-18 20:09:11150 days ago1713470951IN
Loopring: Exchange v2 Fast Withdrawal
0.038565 ETH0.001032619.90496558
Execute Fast Wit...196836692024-04-18 16:55:59150 days ago1713459359IN
Loopring: Exchange v2 Fast Withdrawal
0 ETH0.0017192713.78781148
Execute Fast Wit...196832782024-04-18 15:36:35150 days ago1713454595IN
Loopring: Exchange v2 Fast Withdrawal
0.11834511 ETH0.0020157720.08425271
Execute Fast Wit...196818352024-04-18 10:46:11150 days ago1713437171IN
Loopring: Exchange v2 Fast Withdrawal
0.016826 ETH0.0020783517.26839802
Execute Fast Wit...196818352024-04-18 10:46:11150 days ago1713437171IN
Loopring: Exchange v2 Fast Withdrawal
0.0159367 ETH0.0021507417.26839802
Execute Fast Wit...196818312024-04-18 10:45:23150 days ago1713437123IN
Loopring: Exchange v2 Fast Withdrawal
0.0159367 ETH0.0023021918.48437409
Execute Fast Wit...196804992024-04-18 6:17:11151 days ago1713421031IN
Loopring: Exchange v2 Fast Withdrawal
0.03559137 ETH0.0011476711.00769653
Execute Fast Wit...196787912024-04-18 0:33:35151 days ago1713400415IN
Loopring: Exchange v2 Fast Withdrawal
11.17915509 ETH0.0010302310.26371038
Execute Fast Wit...196780872024-04-17 22:11:23151 days ago1713391883IN
Loopring: Exchange v2 Fast Withdrawal
0.03519795 ETH0.0010801410.35998166
Execute Fast Wit...196770282024-04-17 18:37:23151 days ago1713379043IN
Loopring: Exchange v2 Fast Withdrawal
0 ETH0.002726920.24973104
Execute Fast Wit...196746372024-04-17 10:36:23151 days ago1713350183IN
Loopring: Exchange v2 Fast Withdrawal
0.10440541 ETH0.0011726611.68386276
Execute Fast Wit...196712202024-04-16 23:09:35152 days ago1713308975IN
Loopring: Exchange v2 Fast Withdrawal
0.02956911 ETH0.000726516.96822083
Execute Fast Wit...196707882024-04-16 21:41:59152 days ago1713303719IN
Loopring: Exchange v2 Fast Withdrawal
0.05137515 ETH0.0011794811.31168433
Execute Fast Wit...196706292024-04-16 21:09:47152 days ago1713301787IN
Loopring: Exchange v2 Fast Withdrawal
0.0092575 ETH0.0011459.1932812
Execute Fast Wit...196692442024-04-16 16:30:35152 days ago1713285035IN
Loopring: Exchange v2 Fast Withdrawal
0.0155456 ETH0.0031017430.9101935
Execute Fast Wit...196644502024-04-16 0:23:11153 days ago1713226991IN
Loopring: Exchange v2 Fast Withdrawal
2.36 ETH0.00082918.26164781
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
196923072024-04-19 21:57:59149 days ago1713563879
Loopring: Exchange v2 Fast Withdrawal
60 ETH
196921632024-04-19 21:29:11149 days ago1713562151
Loopring: Exchange v2 Fast Withdrawal
46 ETH
196907542024-04-19 16:45:11149 days ago1713545111
Loopring: Exchange v2 Fast Withdrawal
0.03678 ETH
196905602024-04-19 16:05:47149 days ago1713542747
Loopring: Exchange v2 Fast Withdrawal
0.00005809 ETH
196900242024-04-19 14:17:23149 days ago1713536243
Loopring: Exchange v2 Fast Withdrawal
0.31572295 ETH
196846282024-04-18 20:09:11150 days ago1713470951
Loopring: Exchange v2 Fast Withdrawal
0.038565 ETH
196832782024-04-18 15:36:35150 days ago1713454595
Loopring: Exchange v2 Fast Withdrawal
0.11834511 ETH
196818352024-04-18 10:46:11150 days ago1713437171
Loopring: Exchange v2 Fast Withdrawal
0.016826 ETH
196818352024-04-18 10:46:11150 days ago1713437171
Loopring: Exchange v2 Fast Withdrawal
0.0159367 ETH
196818312024-04-18 10:45:23150 days ago1713437123
Loopring: Exchange v2 Fast Withdrawal
0.0159367 ETH
196804992024-04-18 6:17:11151 days ago1713421031
Loopring: Exchange v2 Fast Withdrawal
0.03559137 ETH
196787912024-04-18 0:33:35151 days ago1713400415
Loopring: Exchange v2 Fast Withdrawal
11.17915509 ETH
196780872024-04-17 22:11:23151 days ago1713391883
Loopring: Exchange v2 Fast Withdrawal
0.03519795 ETH
196746372024-04-17 10:36:23151 days ago1713350183
Loopring: Exchange v2 Fast Withdrawal
0.10440541 ETH
196712202024-04-16 23:09:35152 days ago1713308975
Loopring: Exchange v2 Fast Withdrawal
0.02956911 ETH
196707882024-04-16 21:41:59152 days ago1713303719
Loopring: Exchange v2 Fast Withdrawal
0.05137515 ETH
196706292024-04-16 21:09:47152 days ago1713301787
Loopring: Exchange v2 Fast Withdrawal
0.0092575 ETH
196692442024-04-16 16:30:35152 days ago1713285035
Loopring: Exchange v2 Fast Withdrawal
0.0155456 ETH
196644502024-04-16 0:23:11153 days ago1713226991
Loopring: Exchange v2 Fast Withdrawal
2.36 ETH
196640582024-04-15 23:03:35153 days ago1713222215
Loopring: Exchange v2 Fast Withdrawal
0.17321774 ETH
196622442024-04-15 16:57:23153 days ago1713200243
Loopring: Exchange v2 Fast Withdrawal
0.0033997 ETH
196577642024-04-15 1:53:47154 days ago1713146027
Loopring: Exchange v2 Fast Withdrawal
0.15027045 ETH
196573812024-04-15 0:36:35154 days ago1713141395
Loopring: Exchange v2 Fast Withdrawal
0.069 ETH
196546502024-04-14 15:24:47154 days ago1713108287
Loopring: Exchange v2 Fast Withdrawal
0.09523126 ETH
196513352024-04-14 4:16:23155 days ago1713068183
Loopring: Exchange v2 Fast Withdrawal
0.019 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FastWithdrawalAgent

Compiler Version
v0.7.0+commit.9e61f92b

Optimization Enabled:
Yes with 999999 runs

Other Settings:
default evmVersion, Apache-2.0 license

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

Contract ABI

[{"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"}]

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

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

OVERVIEW

The Fast Withdrawal Agent for Loopring Exchange v2

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
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.