ETH Price: $2,696.05 (-1.51%)
 

Overview

ETH Balance

Scroll LogoScroll LogoScroll Logo0 ETH

ETH Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ZkLink

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./zksync/ReentrancyGuard.sol";
import "./Storage.sol";
import "./zksync/Events.sol";
import "./zksync/UpgradeableMaster.sol";
import "./zksync/Utils.sol";

/// @title ZkLink contract
/// @dev Be carefully to use delegate to split contract(when the code size is too big) code to different files
/// see https://docs.openzeppelin.com/upgrades-plugins/1.x/faq#delegatecall-selfdestruct
/// @dev add `nonReentrant` to all user external interfaces to avoid a closed loop reentrant attack
/// @author zk.link
contract ZkLink is ReentrancyGuard, Storage, Events, UpgradeableMaster {
    using SafeERC20 for IERC20;

    enum ChangePubkeyType {ECRECOVER, CREATE2}

    /// @notice Data needed to process onchain operation from block public data.
    /// @notice Onchain operations is operations that need some processing on L1: Deposits, Withdrawals, ChangePubKey.
    /// @param ethWitness Some external data that can be needed for operation processing
    /// @param publicDataOffset Byte offset in public data for onchain operation
    struct OnchainOperationData {
        bytes ethWitness;
        uint32 publicDataOffset;
    }


    /// @notice Data needed to commit new block
    /// @dev `publicData` contain pubdata of all chains when compressed is disabled or only current chain if compressed is enabled
    /// `onchainOperations` contain onchain ops of all chains when compressed is disabled or only current chain if compressed is enabled
    struct CommitBlockInfo {
        bytes32 newStateHash;
        bytes publicData;
        OnchainOperationData[] onchainOperations;
        uint32 blockNumber;
    }

    /// @notice Data needed to execute committed and verified block
    /// @param storedBlock the block info that will be executed
    /// @param pendingOnchainOpsPubdata onchain ops(e.g. Withdraw, ForcedExit, FullExit) that will be executed
    struct ExecuteBlockInfo {
        StoredBlockInfo storedBlock;
        bytes[] pendingOnchainOpsPubdata;
    }

    /// @dev The periphery code address which is a runtime constant
    address public immutable periphery;

    constructor(address _periphery) {
        periphery = _periphery;
    }

    // =================Upgrade interface=================

    /// @notice Notice period before activation preparation status of upgrade mode
    function getNoticePeriod() external pure override returns (uint256) {
        return UPGRADE_NOTICE_PERIOD;
    }

    /// @notice Checks that contract is ready for upgrade
    /// @return bool flag indicating that contract is ready for upgrade
    function isReadyForUpgrade() external view override returns (bool) {
        return !exodusMode;
    }


    /// @notice ZkLink contract initialization. Can be external because Proxy contract intercepts illegal calls of this function.
    /// @param initializationParameters Encoded representation of initialization parameters:
    /// @dev _verifierAddress The address of Verifier contract
    /// @dev _peripheryAddress The address of ZkLinkPeriphery contract
    /// @dev _networkGovernor The address of system controller
    function initialize(bytes calldata initializationParameters) external onlyDelegateCall {
        initializeReentrancyGuard();

        (address _verifierAddress, address _networkGovernor, uint32 _blockNumber) = abi.decode(initializationParameters, (address, address, uint32));
        require(_verifierAddress != address(0), "i0");
        require(_networkGovernor != address(0), "i2");

        verifier = IVerifier(_verifierAddress);
        networkGovernor = _networkGovernor;

        StoredBlockInfo memory storedBlockZero = StoredBlockInfo(_blockNumber, 0, 0, EMPTY_STRING_KECCAK, EMPTY_STRING_KECCAK);
        storedBlockHashes[0] = hashStoredBlockInfo(storedBlockZero);
    }

    // =================Delegate call=================

    /// @notice Will run when no functions matches call data
    fallback() external payable {
        _fallback(periphery);
    }

    // =================User interface=================

    // =================Validator interface=================


    /// @notice Commit compressed block
    /// @dev 1. Checks onchain operations of current chain, timestamp.
    /// 2. Store block commitments, sync hash
    function commitCompressedBlocks(StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] memory _newBlocksData) external active onlyValidator nonReentrant
    {
        // ===Checks===
        require(_newBlocksData.length > 0, "f0");
        // Check that we commit blocks after last committed block
        uint32 _totalBlocksCommitted = totalBlocksCommitted;
        require(storedBlockHashes[_totalBlocksCommitted] == hashStoredBlockInfo(_lastCommittedBlockData), "f1");

        // ===Effects===
        for (uint32 i = 0; i < _newBlocksData.length; ++i) {
            _lastCommittedBlockData = commitOneCompressedBlock(_lastCommittedBlockData, _newBlocksData[i]);

            // forward `totalCommittedPriorityRequests` because it will be reused in the next `commitOneBlock`
            totalCommittedPriorityRequests = totalCommittedPriorityRequests + _lastCommittedBlockData.priorityOperations;
            storedBlockHashes[++_totalBlocksCommitted] = hashStoredBlockInfo(_lastCommittedBlockData);
        }
        require(totalCommittedPriorityRequests <= totalOpenPriorityRequests, "f2");

        totalBlocksCommitted = _totalBlocksCommitted;
        // log the last new committed block number
        emit BlockCommit(_lastCommittedBlockData.blockNumber);

        totalBlocksSynchronized = _lastCommittedBlockData.blockNumber;
    }

    /// @dev Process one block commit using previous block StoredBlockInfo,
    /// returns new block StoredBlockInfo
    /// NOTE: Does not change storage (except events, so we can't mark it view)
    function commitOneCompressedBlock(StoredBlockInfo memory _previousBlock, CommitBlockInfo memory _newBlock) internal view returns (StoredBlockInfo memory storedNewBlock) {
        require(_newBlock.blockNumber > _previousBlock.blockNumber, "g0");
        // Check onchain operations
        (
            bytes32 pendingOnchainOpsHash,
            uint64 priorityReqCommitted,
            bytes32 onchainOperationPubdataHash
        ) = collectOnchainOpsOfCompressedBlock(_newBlock);

        // Create synchronization hash for cross chain block verify
        bytes32 syncHash = createSlaverChainSyncHash(_previousBlock.syncHash, _newBlock.blockNumber, _newBlock.newStateHash, onchainOperationPubdataHash);

        return StoredBlockInfo(
            _newBlock.blockNumber,
            _previousBlock.blockSequence + 1,
            priorityReqCommitted,
            pendingOnchainOpsHash,
            syncHash
        );
    }

    /// @dev Gets operations packed in bytes array. Unpacks it and stores onchain operations.
    /// Priority operations must be committed in the same order as they are in the priority queue.
    /// NOTE: does not change storage! (only emits events)
    /// processableOperationsHash - hash of all operations of the current chain that needs to be executed  (Withdraws, ForcedExits, FullExits)
    /// priorityOperationsProcessed - number of priority operations processed of the current chain in this block (Deposits, FullExits)
    /// onchainOperationPubdataHash - onchain operation (Deposits, ChangePubKeys, Withdraws, ForcedExits, FullExits) pubdata hash (used in cross chain block verify)
    function collectOnchainOpsOfCompressedBlock(CommitBlockInfo memory _newBlockData) internal view returns (bytes32 processableOperationsHash, uint64 priorityOperationsProcessed, bytes32 onchainOperationPubdataHash) {
        bytes memory pubData = _newBlockData.publicData;
        // pubdata length must be a multiple of CHUNK_BYTES
        require(pubData.length % CHUNK_BYTES == 0, "h0");

        uint64 uncommittedPriorityRequestsOffset = firstPriorityRequestId + totalCommittedPriorityRequests;
        priorityOperationsProcessed = 0;
        onchainOperationPubdataHash = EMPTY_STRING_KECCAK;
        processableOperationsHash = EMPTY_STRING_KECCAK;

        for (uint256 i = 0; i < _newBlockData.onchainOperations.length; ++i) {
            OnchainOperationData memory onchainOpData = _newBlockData.onchainOperations[i];

            uint256 pubdataOffset = onchainOpData.publicDataOffset;
            require(pubdataOffset + 1 < pubData.length, "h1");
            require(pubdataOffset % CHUNK_BYTES == 0, "h2");

            Operations.OpType opType = Operations.OpType(uint8(pubData[pubdataOffset]));

            uint64 nextPriorityOpIndex = uncommittedPriorityRequestsOffset + priorityOperationsProcessed;
            (uint64 newPriorityProceeded, bytes memory opPubData, bytes memory processablePubData) = checkOnchainOpOfCompressedBlock(
                opType,
                pubData,
                pubdataOffset,
                nextPriorityOpIndex,
                onchainOpData.ethWitness);
            priorityOperationsProcessed = priorityOperationsProcessed + newPriorityProceeded;
            onchainOperationPubdataHash = Utils.concatHash(onchainOperationPubdataHash, opPubData);
            if (processablePubData.length > 0) {
                // concat processable onchain operations pubdata hash of current chain
                processableOperationsHash = Utils.concatHash(processableOperationsHash, processablePubData);
            }
        }
    }

    function checkOnchainOpOfCompressedBlock(Operations.OpType opType, bytes memory pubData, uint256 pubdataOffset, uint64 nextPriorityOpIdx, bytes memory ethWitness) internal view returns (uint64 priorityOperationsProcessed, bytes memory opPubData, bytes memory processablePubData) {
        priorityOperationsProcessed = 0;
        processablePubData = new bytes(0);
        // ignore check if ops are not part of the current chain
        if (opType == Operations.OpType.Deposit) {
            opPubData = Bytes.slice(pubData, pubdataOffset, DEPOSIT_BYTES);
            Operations.checkDepositOperation(opPubData, priorityRequests[nextPriorityOpIdx].hashedPubData);
            priorityOperationsProcessed = 1;
        } else if (opType == Operations.OpType.ChangePubKey) {
            opPubData = Bytes.slice(pubData, pubdataOffset, CHANGE_PUBKEY_BYTES);
            Operations.ChangePubKey memory op = Operations.readChangePubKeyPubdata(opPubData);
            if (ethWitness.length != 0) {
                bool valid = verifyChangePubkey(ethWitness, op);
                require(valid, "k0");
            } else {
                bool valid = authFacts[op.owner][op.nonce] == keccak256(abi.encodePacked(op.pubKeyHash));
                require(valid, "k1");
            }
        } else {
            if (opType == Operations.OpType.Withdraw) {
                opPubData = Bytes.slice(pubData, pubdataOffset, WITHDRAW_BYTES);
            } else if (opType == Operations.OpType.ForcedExit) {
                opPubData = Bytes.slice(pubData, pubdataOffset, FORCED_EXIT_BYTES);
            } else if (opType == Operations.OpType.FullExit) {
                opPubData = Bytes.slice(pubData, pubdataOffset, FULL_EXIT_BYTES);
                Operations.checkFullExitOperation(opPubData, priorityRequests[nextPriorityOpIdx].hashedPubData);
                priorityOperationsProcessed = 1;
            } else {
                revert("k2");
            }
            // clone opPubData here instead of return its reference
            // because opPubData and processablePubData will be consumed in later concatHash
            processablePubData = Bytes.slice(opPubData, 0, opPubData.length);
        }
    }

    /// @notice Execute blocks, completing priority operations and processing withdrawals.
    /// @dev 1. Processes all pending operations (Send Exits, Complete priority requests)
    /// 2. Finalizes block on Ethereum
    function executeCompressedBlocks(ExecuteBlockInfo[] memory _blocksData) external active onlyValidator nonReentrant {
        uint32 nBlocks = uint32(_blocksData.length);
        require(nBlocks > 0, "d0");

        uint32 latestExecutedBlockNumber = _blocksData[nBlocks - 1].storedBlock.blockNumber;
        require(latestExecutedBlockNumber <= totalBlocksSynchronized, "d1");

        uint32 _totalBlocksExecuted = totalBlocksExecuted;
        uint64 priorityRequestsExecuted = 0;
        for (uint32 i = 0; i < nBlocks; ++i) {
            uint32 _executedBlockIdx = _totalBlocksExecuted + i + 1;
            ExecuteBlockInfo memory _blockExecuteData = _blocksData[i];
            require(_blockExecuteData.storedBlock.blockSequence == _executedBlockIdx, "d2");

            executeOneBlock(_blockExecuteData, _executedBlockIdx);
            priorityRequestsExecuted = priorityRequestsExecuted + _blockExecuteData.storedBlock.priorityOperations;
        }

        firstPriorityRequestId = firstPriorityRequestId + priorityRequestsExecuted;
        totalCommittedPriorityRequests = totalCommittedPriorityRequests - priorityRequestsExecuted;
        totalOpenPriorityRequests = totalOpenPriorityRequests - priorityRequestsExecuted;

        totalBlocksExecuted = _totalBlocksExecuted + nBlocks;

        emit BlockExecuted(latestExecutedBlockNumber);
    }

    function createSlaverChainSyncHash(bytes32 preBlockSyncHash, uint32 _newBlockNumber, bytes32 _newBlockStateHash, bytes32 _newBlockOnchainOperationPubdataHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(preBlockSyncHash, _newBlockNumber, _newBlockStateHash, _newBlockOnchainOperationPubdataHash));
    }

    // =================Internal functions=================

    function deposit(address _tokenAddress, uint128 _amount, bytes32 _zkLinkAddress, uint8 _subAccountId, bool _mapping) internal active {
        // ===Checks===
        // disable deposit to zero address or global asset account
        require(_zkLinkAddress != bytes32(0) && _zkLinkAddress != GLOBAL_ASSET_ACCOUNT_ADDRESS, "e1");
        // subAccountId MUST be valid
        require(_subAccountId <= MAX_SUB_ACCOUNT_ID, "e2");
        // token MUST be registered to ZkLink and deposit MUST be enabled
        uint16 tokenId = tokenIds[_tokenAddress];
        // 0 is a invalid token and MUST NOT register to zkLink contract
        require(tokenId != 0, "e3");
        RegisteredToken storage rt = tokens[tokenId];
        require(rt.registered, "e3");
        require(!rt.paused, "e4");

        if (_tokenAddress != ETH_ADDRESS) {
            // transfer erc20 token from sender to zkLink contract
            IERC20(_tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
        }

        // improve decimals before send to layer two
        _amount = improveDecimals(_amount, rt.decimals);
        // disable deposit with zero amount
        require(_amount > 0 && _amount <= MAX_DEPOSIT_AMOUNT, "e0");

        // only stable tokens(e.g. USDC, BUSD) support mapping to USD when deposit
        uint16 targetTokenId;
        if (_mapping) {
            require(tokenId >= MIN_USD_STABLE_TOKEN_ID && tokenId <= MAX_USD_STABLE_TOKEN_ID, "e5");
            targetTokenId = USD_TOKEN_ID;
        } else {
            targetTokenId = tokenId;
        }

        // ===Effects===
        // Priority Queue request
        Operations.Deposit memory op =
        Operations.Deposit({
            chainId: CHAIN_ID,
            accountId: 0, // unknown at this point
            subAccountId: _subAccountId,
            owner: _zkLinkAddress,
            tokenId: tokenId,
            targetTokenId: targetTokenId,
            amount: _amount
        });
        bytes memory pubData = Operations.writeDepositPubdataForPriorityQueue(op);
        addPriorityRequest(Operations.OpType.Deposit, pubData, Operations.DEPOSIT_CHECK_BYTES);
    }

    // Priority queue

    /// @notice Saves priority request in storage
    /// @dev Calculates expiration block for request, store this request and emit NewPriorityRequest event
    /// @param _opType Rollup operation type
    /// @param _pubData Operation pubdata
    /// @param _hashSize Operation pubdata hash size
    function addPriorityRequest(Operations.OpType _opType, bytes memory _pubData, uint256 _hashSize) internal {
        // Expiration block is: current block number + priority expiration delta
        uint64 expirationBlock = SafeCast.toUint64(block.number + PRIORITY_EXPIRATION);

        uint64 nextPriorityRequestId = firstPriorityRequestId + totalOpenPriorityRequests;

        bytes20 hashedPubData = Utils.hashBytesWithSizeToBytes20(_pubData, _hashSize);

        priorityRequests[nextPriorityRequestId] = Operations.PriorityOperation({
            hashedPubData: hashedPubData,
            expirationBlock: expirationBlock,
            opType: _opType
        });

        emit NewPriorityRequest(msg.sender, nextPriorityRequestId, _opType, _pubData, uint256(expirationBlock));

        totalOpenPriorityRequests = totalOpenPriorityRequests + 1;
    }

    /// @notice Checks that change operation is correct
    function verifyChangePubkey(bytes memory _ethWitness, Operations.ChangePubKey memory _changePk) internal pure returns (bool) {
        ChangePubkeyType changePkType = ChangePubkeyType(uint8(_ethWitness[0]));
        if (changePkType == ChangePubkeyType.ECRECOVER) {
            return verifyChangePubkeyECRECOVER(_ethWitness, _changePk);
        } else if (changePkType == ChangePubkeyType.CREATE2) {
            return verifyChangePubkeyCREATE2(_ethWitness, _changePk);
        } else {
            return false;
        }
    }

    /// @notice Checks that signature is valid for pubkey change message
    function verifyChangePubkeyECRECOVER(bytes memory _ethWitness, Operations.ChangePubKey memory _changePk) internal pure returns (bool) {
        (, bytes memory signature) = Bytes.read(_ethWitness, 1, 65); // offset is 1 because we skip type of ChangePubkey
        bytes memory message = abi.encodePacked("ChangePubKey\nPubKeyHash: ", Strings.toHexString(uint160(_changePk.pubKeyHash), 20), "\nNonce: ", Strings.toString(_changePk.nonce), "\nAccountId: ", Strings.toString(_changePk.accountId));
        bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(message.length), message));
        address recoveredAddress = Utils.recoverAddressFromEthSignature(signature, messageHash);
        return recoveredAddress == _changePk.owner;
    }

    /// @notice Checks that signature is valid for pubkey change message
    function verifyChangePubkeyCREATE2(bytes memory _ethWitness, Operations.ChangePubKey memory _changePk) internal pure returns (bool) {
        address creatorAddress;
        bytes32 saltArg; // salt arg is additional bytes that are encoded in the CREATE2 salt
        bytes32 codeHash;
        uint256 offset = 1; // offset is 1 because we skip type of ChangePubkey
        (offset, creatorAddress) = Bytes.readAddress(_ethWitness, offset);
        (offset, saltArg) = Bytes.readBytes32(_ethWitness, offset);
        (offset, codeHash) = Bytes.readBytes32(_ethWitness, offset);
        // salt from CREATE2 specification
        bytes32 salt = keccak256(abi.encodePacked(saltArg, _changePk.pubKeyHash));
        // Address computation according to CREATE2 definition: https://eips.ethereum.org/EIPS/eip-1014
        address recoveredAddress = address(
            uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), creatorAddress, salt, codeHash))))
        );
        // This type of change pubkey can be done only once(when the account is not active)
        return recoveredAddress == _changePk.owner && _changePk.nonce == 0;
    }

    /// @dev Executes one block
    /// 1. Processes all pending operations (Send Exits, Complete priority requests)
    /// 2. Finalizes block on Ethereum
    /// _executedBlockIdx is index in the array of the blocks that we want to execute together
    function executeOneBlock(ExecuteBlockInfo memory _blockExecuteData, uint32 _executedBlockIdx) internal {
        // Ensure block was committed
        require(
            hashStoredBlockInfo(_blockExecuteData.storedBlock) ==
            storedBlockHashes[_executedBlockIdx],
            "m0"
        );

        bytes32 pendingOnchainOpsHash = EMPTY_STRING_KECCAK;
        for (uint32 i = 0; i < _blockExecuteData.pendingOnchainOpsPubdata.length; ++i) {
            bytes memory pubData = _blockExecuteData.pendingOnchainOpsPubdata[i];

            Operations.OpType opType = Operations.OpType(uint8(pubData[0]));

            // `pendingOnchainOpsPubdata` only contains ops of the current chain
            // no need to check chain id

            if (opType == Operations.OpType.Withdraw) {
                Operations.Withdraw memory op = Operations.readWithdrawPubdata(pubData);
                // account request fast withdraw and sub account supply nonce
                _executeWithdraw(op.accountId, op.subAccountId, op.nonce, op.owner, op.tokenId, op.amount, op.fastWithdrawFeeRate, op.withdrawToL1);
            } else if (opType == Operations.OpType.ForcedExit) {
                Operations.ForcedExit memory op = Operations.readForcedExitPubdata(pubData);
                // request forced exit for target account but initiator sub account supply nonce
                // forced exit require fast withdraw default and take no fee for fast withdraw
                _executeWithdraw(op.initiatorAccountId, op.initiatorSubAccountId, op.initiatorNonce, op.target, op.tokenId, op.amount, 0, op.withdrawToL1);
            } else if (opType == Operations.OpType.FullExit) {
                Operations.FullExit memory op = Operations.readFullExitPubdata(pubData);
                increasePendingBalance(op.tokenId, op.owner, op.amount);
            } else {
                revert("m2");
            }
            pendingOnchainOpsHash = Utils.concatHash(pendingOnchainOpsHash, pubData);
        }
        require(pendingOnchainOpsHash == _blockExecuteData.storedBlock.pendingOnchainOperationsHash, "m3");
    }

    /// @dev The circuit will check whether there is dust in the amount
    function _executeWithdraw(uint32 accountIdOfNonce, uint8 subAccountIdOfNonce, uint32 nonce, address owner, uint16 tokenId, uint128 amount, uint16 fastWithdrawFeeRate, uint8 withdrawToL1) internal {
        // token MUST be registered
        RegisteredToken storage rt = tokens[tokenId];
        require(rt.registered, "o0");

        // recover withdraw amount
        uint128 recoverAmount = recoveryDecimals(amount, rt.decimals);
        bytes32 withdrawHash = getWithdrawHash(accountIdOfNonce, subAccountIdOfNonce, nonce, owner, rt.tokenAddress, recoverAmount, fastWithdrawFeeRate);
        if (withdrawToL1 == 1) {
            // store L1 withdraw data hash to wait relayer consuming it
            pendingL1Withdraws[withdrawHash] = true;
            emit WithdrawalPendingL1(withdrawHash);
        } else {
            address acceptor = accepts[withdrawHash];
            if (acceptor == address(0)) {
                // receiver act as an acceptor
                accepts[withdrawHash] = owner;
                increasePendingBalance(tokenId, owner, amount);
            } else {
                increasePendingBalance(tokenId, acceptor, amount);
            }
        }
    }

    /// @dev Increase `_recipient` balance to withdraw
    /// @param _amount amount that need to recovery decimals when withdraw
    function increasePendingBalance(uint16 _tokenId, address _recipient, uint128 _amount) internal {
        bytes32 recipient = extendAddress(_recipient);
        increaseBalanceToWithdraw(recipient, _tokenId, _amount);
        emit WithdrawalPending(_tokenId, recipient, _amount);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0


interface IL2Gateway {
    /// @notice Estimate the fee to call send withdraw message
    function estimateWithdrawETHFee(address _owner, uint128 _amount, uint32 _accountIdOfNonce, uint8 _subAccountIdOfNonce, uint32 _nonce, uint16 _fastWithdrawFeeRate) external view returns (uint256 nativeFee);

    /// @notice Withdraw ETH to L1 for owner
    /// @param _owner The address received eth on L1
    /// @param _amount The eth amount received
    /// @param _accountIdOfNonce Account that supply nonce, may be different from accountId
    /// @param _subAccountIdOfNonce SubAccount that supply nonce
    /// @param _nonce SubAccount nonce, used to produce unique accept info
    /// @param _fastWithdrawFeeRate Fast withdraw fee rate taken by acceptor
    function withdrawETH(address _owner, uint128 _amount, uint32 _accountIdOfNonce, uint8 _subAccountIdOfNonce, uint32 _nonce, uint16 _fastWithdrawFeeRate) external payable;

    /// @notice Estimate the fee to call send withdraw message
    function estimateWithdrawERC20Fee(address _owner, address _token, uint128 _amount, uint32 _accountIdOfNonce, uint8 _subAccountIdOfNonce, uint32 _nonce, uint16 _fastWithdrawFeeRate) external view returns (uint256 nativeFee);

    /// @notice Withdraw ERC20 token to L1 for owner
    /// @dev gateway need to pay fee to message service
    /// @param _owner The address received token on L1
    /// @param _token The token address on L2
    /// @param _amount The token amount received
    /// @param _accountIdOfNonce Account that supply nonce, may be different from accountId
    /// @param _subAccountIdOfNonce SubAccount that supply nonce
    /// @param _nonce SubAccount nonce, used to produce unique accept info
    /// @param _fastWithdrawFeeRate Fast withdraw fee rate taken by acceptor
    function withdrawERC20(address _owner, address _token, uint128 _amount, uint32 _accountIdOfNonce, uint8 _subAccountIdOfNonce, uint32 _nonce, uint16 _fastWithdrawFeeRate) external payable;

    /// @notice Return the fee of sending sync hash to ethereum
    /// @param syncHash the sync hash
    function estimateSendSlaverSyncHashFee(bytes32 syncHash) external view returns (uint nativeFee);

    /// @notice Send sync hash message to ethereum
    /// @param syncHash the sync hash
    function sendSlaverSyncHash(bytes32 syncHash) external payable;

    /// @notice Return the fee of sending sync hash to ethereum
    /// @param blockNumber the block number
    /// @param syncHash the sync hash
    function estimateSendMasterSyncHashFee(uint32 blockNumber, bytes32 syncHash) external view returns (uint nativeFee);

    /// @notice Send sync hash message to ethereum
    /// @param blockNumber the block number
    /// @param syncHash the sync hash
    function sendMasterSyncHash(uint32 blockNumber, bytes32 syncHash) external payable;
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



/// @title Sync service for sending cross chain message
/// @author zk.link
interface ISyncService {
    /// @notice Return the fee of sending sync hash to master chain
    /// @param syncHash the sync hash
    function estimateSendSyncHashFee(bytes32 syncHash) external view returns (uint nativeFee);

    /// @notice Send sync hash message to master chain
    /// @param syncHash the sync hash
    function sendSyncHash(bytes32 syncHash) external payable;

    /// @notice Estimate the fee of sending confirm block message to slaver chain
    /// @param destZkLinkChainId the destination chain id defined by zkLink
    /// @param blockNumber the height of stored block
    function estimateConfirmBlockFee(uint8 destZkLinkChainId, uint32 blockNumber) external view returns (uint nativeFee);

    /// @notice Send block confirmation message to slaver chains
    /// @param destZkLinkChainId the destination chain id defined by zkLink
    /// @param blockNumber the block height
    function confirmBlock(uint8 destZkLinkChainId, uint32 blockNumber) external payable;
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



/// @title Verifier interface contract
/// @author zk.link
interface IVerifier {
    function verifyAggregatedBlockProof(uint256[] memory _recursiveInput, uint256[] memory _proof, uint8[] memory _vkIndexes, uint256[] memory _individualVksInputs, uint256[16] memory _subProofsLimbs) external returns (bool);

    function verifyExitProof(bytes32 _rootHash, uint8 _chainId, uint32 _accountId, uint8 _subAccountId, bytes32 _owner, uint16 _tokenId, uint16 _srcTokenId, uint128 _amount, uint256[] calldata _proof) external returns (bool);
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



import "./zksync/Operations.sol";
import "./zksync/Config.sol";
import "./interfaces/IVerifier.sol";
import "./interfaces/ISyncService.sol";
import "./interfaces/IL2Gateway.sol";
import "./zksync/SafeCast.sol";
import "./ZkLinkAcceptor.sol";

/// @title ZkLink storage contract
/// @dev Be carefully to change the order of variables
/// @author zk.link
contract Storage is ZkLinkAcceptor, Config {
    /// @dev Used to safely call `delegatecall`, immutable state variables don't occupy storage slot
    address internal immutable self = address(this);

    // verifier(20 bytes) + totalBlocksExecuted(4 bytes) + firstPriorityRequestId(8 bytes) stored in the same slot

    /// @notice Verifier contract. Used to verify block proof and exit proof
    IVerifier public verifier;

    /// @notice Total number of executed blocks i.e. blocks[totalBlocksExecuted] points at the latest executed block (block 0 is genesis)
    uint32 public totalBlocksExecuted;

    /// @notice First open priority request id
    uint64 public firstPriorityRequestId;

    // networkGovernor(20 bytes) + totalBlocksCommitted(4 bytes) + totalOpenPriorityRequests(8 bytes) stored in the same slot

    /// @notice The the owner of whole system
    address public networkGovernor;

    /// @notice Total number of committed blocks i.e. blocks[totalBlocksCommitted] points at the latest committed block
    uint32 public totalBlocksCommitted;

    /// @notice Total number of requests
    uint64 public totalOpenPriorityRequests;

    // gateway(20 bytes) + totalBlocksProven(4 bytes) + totalCommittedPriorityRequests(8 bytes) stored in the same slot

    /// @notice The gateway is used for communicating with L1
    /// @dev The gateway will not be set if local chain is a L1
    IL2Gateway public gateway;

    /// @notice Total blocks proven.
    uint32 public totalBlocksProven;

    /// @notice Total number of committed requests.
    /// @dev Used in checks: if the request matches the operation on Rollup contract and if provided number of requests is not too big
    uint64 public totalCommittedPriorityRequests;

    // totalBlocksSynchronized(4 bytes) + exodusMode(1 bytes) stored in the same slot

    /// @dev Latest synchronized block height
    uint32 public totalBlocksSynchronized;

    /// @notice Flag indicates that exodus (mass exit) mode is triggered
    /// @notice Once it was raised, it can not be cleared again, and all users must exit
    bool public exodusMode;

    /// @dev Root-chain balances (per owner and token id) to withdraw
    /// @dev the amount of pending balance need to recovery decimals when withdraw
    /// @dev The struct of this map is (owner => tokenId => balance)
    /// @dev The type of owner is bytes32, when storing evm address, 12 bytes of prefix zero will be appended
    /// @dev for example: 0x000000000000000000000000A1a547358A9Ca8E7b320d7742729e3334Ad96546
    mapping(bytes32 => mapping(uint16 => uint128)) internal pendingBalances;

    /// @dev Store withdraw data hash that need to be relayed to L1 by gateway
    /// @dev The key is the withdraw data hash
    /// @dev The value is a flag to indicating whether withdraw exists
    mapping(bytes32 => bool) public pendingL1Withdraws;

    /// @notice Flag indicates that a user has exited a certain token balance in the exodus mode
    /// @dev The struct of this map is (accountId => subAccountId => withdrawTokenId => deductTokenId => performed)
    /// @dev withdrawTokenId is the token that withdraw to user in L1
    /// @dev deductTokenId is the token that deducted from user in L2
    mapping(uint32 => mapping(uint8 => mapping(uint16 => mapping(uint16 => bool)))) public performedExodus;

    /// @dev Priority Requests mapping (request id - operation)
    /// Contains op type, pubdata and expiration block of unsatisfied requests.
    /// Numbers are in order of requests receiving
    mapping(uint64 => Operations.PriorityOperation) public priorityRequests;

    /// @notice User authenticated fact hashes for some nonce.
    mapping(address => mapping(uint32 => bytes32)) public authFacts;

    /// @dev Timer for authFacts entry reset (address, nonce -> timer).
    /// Used when user wants to reset `authFacts` for some nonce.
    mapping(address => mapping(uint32 => uint256)) public authFactsResetTimer;

    /// @dev Stored hashed StoredBlockInfo for some block number
    mapping(uint32 => bytes32) public storedBlockHashes;

    /// @dev Store sync hash for slaver chains
    /// chainId => syncHash
    mapping(uint8 => bytes32) public synchronizedChains;

    /// @notice A set of permitted validators
    mapping(address => bool) public validators;

    struct RegisteredToken {
        bool registered; // whether token registered to ZkLink or not, default is false
        bool paused; // whether token can deposit to ZkLink or not, default is false
        address tokenAddress; // the token address
        uint8 decimals; // the token decimals of layer one
    }

    /// @notice A map of registered token infos
    mapping(uint16 => RegisteredToken) public tokens;

    /// @notice A map of token address to id, 0 is invalid token id
    mapping(address => uint16) public tokenIds;

    /// @dev Support multiple sync services, for example:
    /// <Linea, zkSync Era> - LayerZero
    /// <Linea, Scroll> - zkBridge
    /// chainId => sync service
    mapping(uint8 => ISyncService) public chainSyncServiceMap;
    mapping(address => bool) public syncServiceMap;


    /// @notice block stored data
    struct StoredBlockInfo {
        uint32 blockNumber; // Rollup block number
        uint32 blockSequence; // The block commit sequence
        uint64 priorityOperations; // Number of priority operations processed
        bytes32 pendingOnchainOperationsHash; // Hash of all operations that must be processed after verify
        bytes32 syncHash; // Used for cross chain block verify
    }

    /// @notice Checks that current state not is exodus mode
    modifier active() {
        require(!exodusMode, "0");
        _;
    }

    /// @notice Checks that current state is exodus mode
    modifier notActive() {
        require(exodusMode, "1");
        _;
    }

    /// @notice Set logic contract must be called through proxy
    modifier onlyDelegateCall() {
        require(address(this) != self, "2");
        _;
    }

    modifier onlyGovernor {
        require(msg.sender == networkGovernor, "3");
        _;
    }

    /// @notice Check if msg sender is a validator
    modifier onlyValidator() {
        require(validators[msg.sender], "4");
        _;
    }

    /// @notice Check if msg sender is sync service
    modifier onlySyncService() {
        require(syncServiceMap[msg.sender], "6");
        _;
    }

    /// @notice Check if msg sender is gateway
    modifier onlyGateway() {
        require(msg.sender == address(gateway), "7");
        _;
    }

    /// @notice Returns the keccak hash of the ABI-encoded StoredBlockInfo
    function hashStoredBlockInfo(StoredBlockInfo memory _storedBlockInfo) internal pure returns (bytes32) {
        return keccak256(abi.encode(_storedBlockInfo));
    }

    /// @notice Increase pending balance to withdraw
    /// @param _address the pending balance owner
    /// @param _tokenId token id
    /// @param _amount pending amount that need to recovery decimals when withdraw
    function increaseBalanceToWithdraw(bytes32 _address, uint16 _tokenId, uint128 _amount) internal {
        uint128 balance = pendingBalances[_address][_tokenId];
        // overflow should not happen here
        // (2^128 / 10^18 = 3.4 * 10^20) is enough to meet the really token balance of L2 account
        pendingBalances[_address][_tokenId] = balance + _amount;
    }

    /// @notice Extend address to bytes32
    /// @dev for example: extend 0xA1a547358A9Ca8E7b320d7742729e3334Ad96546 and the result is 0x000000000000000000000000a1a547358a9ca8e7b320d7742729e3334ad96546
    function extendAddress(address _address) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(_address)));
    }

    /// @dev improve decimals when deposit, for example, user deposit 2 USDC in ui, and the decimals of USDC is 6
    /// the `_amount` params when call contract will be 2 * 10^6
    /// because all token decimals defined in layer two is 18
    /// so the `_amount` in deposit pubdata should be 2 * 10^6 * 10^(18 - 6) = 2 * 10^18
    function improveDecimals(uint128 _amount, uint8 _decimals) internal pure returns (uint128) {
        // overflow is impossible,  `_decimals` has been checked when register token
        return _amount * SafeCast.toUint128(10**(TOKEN_DECIMALS_OF_LAYER2 - _decimals));
    }

    /// @dev recover decimals when withdraw, this is the opposite of improve decimals
    function recoveryDecimals(uint128 _amount, uint8 _decimals) internal pure returns (uint128) {
        // overflow is impossible,  `_decimals` has been checked when register token
        return _amount / SafeCast.toUint128(10**(TOKEN_DECIMALS_OF_LAYER2 - _decimals));
    }

    /// @notice Performs a delegatecall to the contract implementation
    /// @dev Fallback function allowing to perform a delegatecall to the given implementation
    /// This function will return whatever the implementation call returns
    function _fallback(address _target) internal {
        require(_target != address(0), "5");
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), _target, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title ZkLink acceptor contract
/// @author zk.link
abstract contract ZkLinkAcceptor {
    using SafeERC20 for IERC20;

    /// @dev Address represent eth when deposit or withdraw
    address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /// @dev When set fee = 100, it means 1%
    uint16 internal constant MAX_ACCEPT_FEE_RATE = 10000;

    /// @dev Accept infos of withdraw
    /// @dev key is keccak256(abi.encodePacked(accountIdOfNonce, subAccountIdOfNonce, nonce, owner, token, amount, fastWithdrawFeeRate))
    /// @dev value is the acceptor
    mapping(bytes32 => address) public accepts;

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

    /// @notice Event emitted when acceptor accept a fast withdraw
    event Accept(address acceptor, address receiver, address token, uint128 amount, uint16 withdrawFeeRate, uint32 accountIdOfNonce, uint8 subAccountIdOfNonce, uint32 nonce, uint128 amountReceive);

    /// @notice Acceptor accept a eth fast withdraw, acceptor will get a fee for profit
    /// @param receiver User receive token from acceptor (the owner of withdraw operation)
    /// @param amount The amount of withdraw operation
    /// @param fastWithdrawFeeRate Fast withdraw fee rate taken by acceptor
    /// @param accountIdOfNonce Account that supply nonce
    /// @param subAccountIdOfNonce SubAccount that supply nonce
    /// @param nonce SubAccount nonce, used to produce unique accept info
    function acceptETH(address payable receiver, uint128 amount, uint16 fastWithdrawFeeRate, uint32 accountIdOfNonce, uint8 subAccountIdOfNonce, uint32 nonce) external payable {
        // ===Checks===
        uint128 amountReceive = _checkAccept(msg.sender, receiver, ETH_ADDRESS, amount, fastWithdrawFeeRate, accountIdOfNonce, subAccountIdOfNonce, nonce);

        // ===Interactions===
        // make sure msg value >= amountReceive
        uint256 amountReturn = msg.value - amountReceive;
        // msg.sender should set a reasonable gas limit when call this function
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, ) = receiver.call{value: amountReceive}("");
        require(success, "E0");
        // if send too more eth then return back to msg sender
        if (amountReturn > 0) {
            // it's safe to use call to msg.sender and can send all gas left to it
            // solhint-disable-next-line avoid-low-level-calls
            (success, ) = msg.sender.call{value: amountReturn}("");
            require(success, "E1");
        }
        emit Accept(msg.sender, receiver, ETH_ADDRESS, amount, fastWithdrawFeeRate, accountIdOfNonce, subAccountIdOfNonce, nonce, amountReceive);
    }

    /// @notice Acceptor accept a erc20 token fast withdraw, acceptor will get a fee for profit
    /// @param receiver User receive token from acceptor (the owner of withdraw operation)
    /// @param token Token address
    /// @param amount The amount of withdraw operation
    /// @param fastWithdrawFeeRate Fast withdraw fee rate taken by acceptor
    /// @param accountIdOfNonce Account that supply nonce
    /// @param subAccountIdOfNonce SubAccount that supply nonce
    /// @param nonce SubAccount nonce, used to produce unique accept info
    function acceptERC20(address receiver, address token, uint128 amount, uint16 fastWithdrawFeeRate, uint32 accountIdOfNonce, uint8 subAccountIdOfNonce, uint32 nonce) external {
        // ===Checks===
        uint128 amountReceive = _checkAccept(msg.sender, receiver, token, amount, fastWithdrawFeeRate, accountIdOfNonce, subAccountIdOfNonce, nonce);

        // ===Interactions===
        IERC20(token).safeTransferFrom(msg.sender, receiver, amountReceive);
        emit Accept(msg.sender, receiver, token, amount, fastWithdrawFeeRate, accountIdOfNonce, subAccountIdOfNonce, nonce, amountReceive);
    }

    function _checkAccept(address acceptor, address receiver, address token, uint128 amount, uint16 fastWithdrawFeeRate, uint32 accountIdOfNonce, uint8 subAccountIdOfNonce, uint32 nonce) internal returns (uint128 amountReceive) {
        // acceptor and receiver MUST be set and MUST not be the same
        require(receiver != address(0), "H1");
        require(receiver != acceptor, "H2");
        // feeRate MUST be valid and MUST not be 100%
        require(fastWithdrawFeeRate < MAX_ACCEPT_FEE_RATE, "H3");
        amountReceive = amount * (MAX_ACCEPT_FEE_RATE - fastWithdrawFeeRate) / MAX_ACCEPT_FEE_RATE;

        // accept tx may be later than block exec tx(with user withdraw op)
        bytes32 hash = getWithdrawHash(accountIdOfNonce, subAccountIdOfNonce, nonce, receiver, token, amount, fastWithdrawFeeRate);
        require(accepts[hash] == address(0), "H4");

        // ===Effects===
        accepts[hash] = acceptor;
    }

    /// @dev Return accept record hash for withdraw
    /// @dev (accountIdOfNonce, subAccountIdOfNonce, nonce) ensures the uniqueness of withdraw hash
    function getWithdrawHash(uint32 accountIdOfNonce, uint8 subAccountIdOfNonce, uint32 nonce, address owner, address token, uint128 amount, uint16 fastWithdrawFeeRate) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(accountIdOfNonce, subAccountIdOfNonce, nonce, owner, token, amount, fastWithdrawFeeRate));
    }

    /// @dev Update the receiver of withdraw claim
    /// @dev If acceptor accepted this withdraw then return acceptor or return owner
    function updateAcceptReceiver(address _owner, address _token, uint128 _amount, uint32 _accountIdOfNonce, uint8 _subAccountIdOfNonce, uint32 _nonce, uint16 _fastWithdrawFeeRate) internal returns (address) {
        bytes32 withdrawHash = getWithdrawHash(_accountIdOfNonce, _subAccountIdOfNonce, _nonce, _owner, _token, _amount, _fastWithdrawFeeRate);
        address acceptor = accepts[withdrawHash];
        address receiver = acceptor;
        if (acceptor == address(0)) {
            // receiver act as a acceptor
            receiver = _owner;
            accepts[withdrawHash] = _owner;
        }
        return receiver;
    }
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



// Functions named bytesToX, except bytesToBytes20, where X is some type of size N < 32 (size of one word)
// implements the following algorithm:
// f(bytes memory input, uint offset) -> X out
// where byte representation of out is N bytes from input at the given offset
// 1) We compute memory location of the word W such that last N bytes of W is input[offset..offset+N]
// W_address = input + 32 (skip stored length of bytes) + offset - (32 - N) == input + offset + N
// 2) We load W from memory into out, last N bytes of W are placed into out

library Bytes {
    function toBytesFromUInt16(uint16 self) internal pure returns (bytes memory _bts) {
        return toBytesFromUIntTruncated(uint256(self), 2);
    }

    function toBytesFromUInt24(uint24 self) internal pure returns (bytes memory _bts) {
        return toBytesFromUIntTruncated(uint256(self), 3);
    }

    function toBytesFromUInt32(uint32 self) internal pure returns (bytes memory _bts) {
        return toBytesFromUIntTruncated(uint256(self), 4);
    }

    function toBytesFromUInt128(uint128 self) internal pure returns (bytes memory _bts) {
        return toBytesFromUIntTruncated(uint256(self), 16);
    }

    // Copies 'len' lower bytes from 'self' into a new 'bytes memory'.
    // Returns the newly created 'bytes memory'. The returned bytes will be of length 'len'.
    function toBytesFromUIntTruncated(uint256 self, uint8 byteLength) private pure returns (bytes memory bts) {
        require(byteLength <= 32, "Q");
        bts = new bytes(byteLength);
        // Even though the bytes will allocate a full word, we don't want
        // any potential garbage bytes in there.
        uint256 data = self << ((32 - byteLength) * 8);
        assembly {
            mstore(
            add(bts, 32), // BYTES_HEADER_SIZE
            data
            )
        }
    }

    // Copies 'self' into a new 'bytes memory'.
    // Returns the newly created 'bytes memory'. The returned bytes will be of length '20'.
    function toBytesFromAddress(address self) internal pure returns (bytes memory bts) {
        bts = toBytesFromUIntTruncated(uint256(uint160(self)), 20);
    }

    // See comment at the top of this file for explanation of how this function works.
    // NOTE: theoretically possible overflow of (_start + 20)
    function bytesToAddress(bytes memory self, uint256 _start) internal pure returns (address addr) {
        uint256 offset = _start + 20;
        require(self.length >= offset, "R");
        assembly {
            addr := mload(add(self, offset))
        }
    }

    // Reasoning about why this function works is similar to that of other similar functions, except NOTE below.
    // NOTE: that bytes1..32 is stored in the beginning of the word unlike other primitive types
    // NOTE: theoretically possible overflow of (_start + 20)
    function bytesToBytes20(bytes memory self, uint256 _start) internal pure returns (bytes20 r) {
        require(self.length >= (_start + 20), "S");
        assembly {
            r := mload(add(add(self, 0x20), _start))
        }
    }

    // See comment at the top of this file for explanation of how this function works.
    // NOTE: theoretically possible overflow of (_start + 0x2)
    function bytesToUInt16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 r) {
        uint256 offset = _start + 0x2;
        require(_bytes.length >= offset, "T");
        assembly {
            r := mload(add(_bytes, offset))
        }
    }

    // See comment at the top of this file for explanation of how this function works.
    // NOTE: theoretically possible overflow of (_start + 0x3)
    function bytesToUInt24(bytes memory _bytes, uint256 _start) internal pure returns (uint24 r) {
        uint256 offset = _start + 0x3;
        require(_bytes.length >= offset, "U");
        assembly {
            r := mload(add(_bytes, offset))
        }
    }

    // NOTE: theoretically possible overflow of (_start + 0x4)
    function bytesToUInt32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 r) {
        uint256 offset = _start + 0x4;
        require(_bytes.length >= offset, "V");
        assembly {
            r := mload(add(_bytes, offset))
        }
    }

    // NOTE: theoretically possible overflow of (_start + 0x10)
    function bytesToUInt128(bytes memory _bytes, uint256 _start) internal pure returns (uint128 r) {
        uint256 offset = _start + 0x10;
        require(_bytes.length >= offset, "W");
        assembly {
            r := mload(add(_bytes, offset))
        }
    }

    // See comment at the top of this file for explanation of how this function works.
    // NOTE: theoretically possible overflow of (_start + 0x14)
    function bytesToUInt160(bytes memory _bytes, uint256 _start) internal pure returns (uint160 r) {
        uint256 offset = _start + 0x14;
        require(_bytes.length >= offset, "X");
        assembly {
            r := mload(add(_bytes, offset))
        }
    }

    // NOTE: theoretically possible overflow of (_start + 0x20)
    function bytesToBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 r) {
        uint256 offset = _start + 0x20;
        require(_bytes.length >= offset, "Y");
        assembly {
            r := mload(add(_bytes, offset))
        }
    }

    // Original source code: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol#L228
    // Get slice from bytes arrays
    // Returns the newly created 'bytes memory'
    // NOTE: theoretically possible overflow of (_start + _length)
    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    ) internal pure returns (bytes memory) {
        require(_bytes.length >= (_start + _length), "Z"); // bytes length is less then start byte + length bytes

        bytes memory tempBytes = new bytes(_length);

        if (_length != 0) {
            assembly {
                let slice_curr := add(tempBytes, 0x20)
                let slice_end := add(slice_curr, _length)

                for {
                    let array_current := add(_bytes, add(_start, 0x20))
                } lt(slice_curr, slice_end) {
                    slice_curr := add(slice_curr, 0x20)
                    array_current := add(array_current, 0x20)
                } {
                    mstore(slice_curr, mload(array_current))
                }
            }
        }

        return tempBytes;
    }

    /// Reads byte stream
    /// @return newOffset - offset + amount of bytes read
    /// @return data - actually read data
    // NOTE: theoretically possible overflow of (_offset + _length)
    function read(
        bytes memory _data,
        uint256 _offset,
        uint256 _length
    ) internal pure returns (uint256 newOffset, bytes memory data) {
        data = slice(_data, _offset, _length);
        newOffset = _offset + _length;
    }

    // NOTE: theoretically possible overflow of (_offset + 1)
    function readBool(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, bool r) {
        newOffset = _offset + 1;
        r = uint8(_data[_offset]) != 0;
    }

    // NOTE: theoretically possible overflow of (_offset + 1)
    function readUint8(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint8 r) {
        newOffset = _offset + 1;
        r = uint8(_data[_offset]);
    }

    // NOTE: theoretically possible overflow of (_offset + 2)
    function readUInt16(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint16 r) {
        newOffset = _offset + 2;
        r = bytesToUInt16(_data, _offset);
    }

    // NOTE: theoretically possible overflow of (_offset + 3)
    function readUInt24(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint24 r) {
        newOffset = _offset + 3;
        r = bytesToUInt24(_data, _offset);
    }

    // NOTE: theoretically possible overflow of (_offset + 4)
    function readUInt32(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint32 r) {
        newOffset = _offset + 4;
        r = bytesToUInt32(_data, _offset);
    }

    // NOTE: theoretically possible overflow of (_offset + 16)
    function readUInt128(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint128 r) {
        newOffset = _offset + 16;
        r = bytesToUInt128(_data, _offset);
    }

    // NOTE: theoretically possible overflow of (_offset + 20)
    function readUInt160(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint160 r) {
        newOffset = _offset + 20;
        r = bytesToUInt160(_data, _offset);
    }

    // NOTE: theoretically possible overflow of (_offset + 20)
    function readAddress(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, address r) {
        newOffset = _offset + 20;
        r = bytesToAddress(_data, _offset);
    }

    // NOTE: theoretically possible overflow of (_offset + 20)
    function readBytes20(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, bytes20 r) {
        newOffset = _offset + 20;
        r = bytesToBytes20(_data, _offset);
    }

    // NOTE: theoretically possible overflow of (_offset + 32)
    function readBytes32(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, bytes32 r) {
        newOffset = _offset + 32;
        r = bytesToBytes32(_data, _offset);
    }

    /// Trim bytes into single word
    function trim(bytes memory _data, uint256 _newLength) internal pure returns (uint256 r) {
        require(_newLength <= 0x20, "10"); // new_length is longer than word
        require(_data.length >= _newLength, "11"); // data is to short

        uint256 a;
        assembly {
            a := mload(add(_data, 0x20)) // load bytes into uint256
        }

        return a >> ((0x20 - _newLength) * 8);
    }

    // Helper function for hex conversion.
    function halfByteToHex(bytes1 _byte) internal pure returns (bytes1 _hexByte) {
        require(uint8(_byte) < 0x10, "hbh11"); // half byte's value is out of 0..15 range.

        // "FEDCBA9876543210" ASCII-encoded, shifted and automatically truncated.
        return bytes1(uint8(0x66656463626139383736353433323130 >> (uint8(_byte) * 8)));
    }

    // Convert bytes to ASCII hex representation
    function bytesToHexASCIIBytes(bytes memory _input) internal pure returns (bytes memory _output) {
        bytes memory outStringBytes = new bytes(_input.length * 2);

        // code in `assembly` construction is equivalent of the next code:
        // for (uint i = 0; i < _input.length; ++i) {
        //     outStringBytes[i*2] = halfByteToHex(_input[i] >> 4);
        //     outStringBytes[i*2+1] = halfByteToHex(_input[i] & 0x0f);
        // }
        assembly {
            let input_curr := add(_input, 0x20)
            let input_end := add(input_curr, mload(_input))

            for {
                let out_curr := add(outStringBytes, 0x20)
            } lt(input_curr, input_end) {
                input_curr := add(input_curr, 0x01)
                out_curr := add(out_curr, 0x02)
            } {
                let curr_input_byte := shr(0xf8, mload(input_curr))
            // here outStringByte from each half of input byte calculates by the next:
            //
            // "FEDCBA9876543210" ASCII-encoded, shifted and automatically truncated.
            // outStringByte = byte (uint8 (0x66656463626139383736353433323130 >> (uint8 (_byteHalf) * 8)))
                mstore(
                out_curr,
                shl(0xf8, shr(mul(shr(0x04, curr_input_byte), 0x08), 0x66656463626139383736353433323130))
                )
                mstore(
                add(out_curr, 0x01),
                shl(0xf8, shr(mul(and(0x0f, curr_input_byte), 0x08), 0x66656463626139383736353433323130))
                )
            }
        }
        return outStringBytes;
    }
}

File 15 of 22 : Config.sol
pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



/// @title zkSync configuration constants
/// @author Matter Labs
contract Config {
    /// @dev Default fee address in state
    address public constant DEFAULT_FEE_ADDRESS = 0x374632e7D48B7872d904524FdC5Dd4516F42cDFF;

    bytes32 internal constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;

    /// @dev Bytes in one chunk
    uint8 internal constant CHUNK_BYTES = 23;

    /// @dev Bytes of L2 PubKey hash
    uint8 internal constant PUBKEY_HASH_BYTES = 20;

    /// @dev Max amount of tokens registered in the network
    uint16 internal constant MAX_AMOUNT_OF_REGISTERED_TOKENS = 65535;

    /// @dev Max account id that could be registered in the network
    uint32 internal constant MAX_ACCOUNT_ID = 16777215;

    /// @dev Max sub account id that could be bound to account id
    uint8 internal constant MAX_SUB_ACCOUNT_ID = 31;

    /// @dev Expected average period of block creation
    uint256 internal constant BLOCK_PERIOD = 3 seconds;

    /// @dev Operation chunks
    uint256 internal constant DEPOSIT_BYTES = 3 * CHUNK_BYTES;
    uint256 internal constant FULL_EXIT_BYTES = 3 * CHUNK_BYTES;
    uint256 internal constant WITHDRAW_BYTES = 3 * CHUNK_BYTES;
    uint256 internal constant FORCED_EXIT_BYTES = 3 * CHUNK_BYTES;
    uint256 internal constant CHANGE_PUBKEY_BYTES = 3 * CHUNK_BYTES;

    /// @dev Expiration delta for priority request to be satisfied (in seconds)
    /// @dev NOTE: Priority expiration should be > (EXPECT_VERIFICATION_IN * BLOCK_PERIOD)
    /// @dev otherwise incorrect block with priority op could not be reverted.
    uint256 internal constant PRIORITY_EXPIRATION_PERIOD = 14 days;

    /// @dev Expiration delta for priority request to be satisfied (in ETH blocks)
    uint256 internal constant PRIORITY_EXPIRATION =
        864000;

    /// @dev Reserved time for users to send full exit priority operation in case of an upgrade (in seconds)
    uint256 internal constant MASS_FULL_EXIT_PERIOD = 5 days;

    /// @dev Reserved time for users to withdraw funds from full exit priority operation in case of an upgrade (in seconds)
    uint256 internal constant TIME_TO_WITHDRAW_FUNDS_FROM_FULL_EXIT = 2 days;

    /// @dev Notice period before activation preparation status of upgrade mode (in seconds)
    /// @dev NOTE: we must reserve for users enough time to send full exit operation, wait maximum time for processing this operation and withdraw funds from it.
    uint256 internal constant UPGRADE_NOTICE_PERIOD =
        0;

    /// @dev Max commitment produced in zk proof where highest 3 bits is 0
    uint256 internal constant MAX_PROOF_COMMITMENT = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

    /// @dev Bit mask to apply for verifier public input before verifying.
    uint256 internal constant INPUT_MASK = 14474011154664524427946373126085988481658748083205070504932198000989141204991;

    /// @dev Auth fact reset timelock
    uint256 internal constant AUTH_FACT_RESET_TIMELOCK = 1 days;

    /// @dev Max deposit of ERC20 token that is possible to deposit
    uint128 internal constant MAX_DEPOSIT_AMOUNT = 20282409603651670423947251286015;

    /// @dev Chain id defined by ZkLink
    uint8 public constant CHAIN_ID = 6;

    /// @dev Min chain id defined by ZkLink
    uint8 public constant MIN_CHAIN_ID = 1;

    /// @dev Max chain id defined by ZkLink
    uint8 public constant MAX_CHAIN_ID = 8;

    /// @dev All chain index, for example [1, 2, 3, 4] => 1 << 0 | 1 << 1 | 1 << 2 | 1 << 3 = 15
    uint256 public constant ALL_CHAINS = 240;

    /// @dev Master chain id defined by ZkLink
    uint8 public constant MASTER_CHAIN_ID = 7;

    /// @dev NONE, ORIGIN, NEXUS
    uint8 internal constant SYNC_TYPE = 0;
    uint8 internal constant SYNC_NONE = 0;
    uint8 internal constant SYNC_ORIGIN = 1;
    uint8 internal constant SYNC_NEXUS = 2;

    /// @dev Token decimals is a fixed value at layer two in ZkLink
    uint8 internal constant TOKEN_DECIMALS_OF_LAYER2 = 18;

    /// @dev The default fee account id
    uint32 internal constant DEFAULT_FEE_ACCOUNT_ID = 0;

    /// @dev Global asset account in the network
    /// @dev Can not deposit to or full exit this account
    uint32 internal constant GLOBAL_ASSET_ACCOUNT_ID = 1;
    bytes32 internal constant GLOBAL_ASSET_ACCOUNT_ADDRESS = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

    /// @dev USD and USD stable tokens defined by zkLink
    /// @dev User can deposit USD stable token(eg. USDC, BUSD) to get USD in layer two
    /// @dev And user also can full exit USD in layer two and get back USD stable tokens
    uint16 internal constant USD_TOKEN_ID = 1;
    uint16 internal constant MIN_USD_STABLE_TOKEN_ID = 17;
    uint16 internal constant MAX_USD_STABLE_TOKEN_ID = 31;
}

File 16 of 22 : Events.sol
pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



import "./Upgradeable.sol";
import "./Operations.sol";

/// @title zkSync events
/// @author Matter Labs
interface Events {
    /// @notice Event emitted when a block is committed
    event BlockCommit(uint32 indexed blockNumber);

    /// @notice Event emitted when a block is proven
    event BlockProven(uint32 indexed blockNumber);

    /// @notice Event emitted when a block is synced
    event BlockSynced(uint32 indexed blockNumber);

    /// @notice Event emitted when a block is executed
    event BlockExecuted(uint32 indexed blockNumber);

    /// @notice Event emitted when user funds are withdrawn from the zkLink state and contract
    event Withdrawal(uint16 indexed tokenId, uint128 amount);

    /// @notice Event emitted when user funds are withdrawn from the zkLink state but not from contract
    event WithdrawalPending(uint16 indexed tokenId, bytes32 indexed recepient, uint128 amount);

    /// @notice Event emitted when user funds are withdrawn from the zkLink state to L1 and contract
    event WithdrawalL1(bytes32 indexed withdrawHash);

    /// @notice Event emitted when user funds are withdrawn from the zkLink state to L1 but not from contract
    event WithdrawalPendingL1(bytes32 indexed withdrawHash);

    /// @notice Event emitted when user sends a authentication fact (e.g. pub-key hash)
    event FactAuth(address indexed sender, uint32 nonce, bytes fact);

    /// @notice Event emitted when authentication fact reset clock start
    event FactAuthResetTime(address indexed sender, uint32 nonce, uint256 time);

    /// @notice Event emitted when blocks are reverted
    event BlocksRevert(uint32 totalBlocksVerified, uint32 totalBlocksCommitted);

    /// @notice Exodus mode entered event
    event ExodusMode();

    /// @notice New priority request event. Emitted when a request is placed into mapping
    event NewPriorityRequest(
        address sender,
        uint64 serialId,
        Operations.OpType opType,
        bytes pubData,
        uint256 expirationBlock
    );

    /// @notice Token added to ZkLink net
    /// @dev log token decimals on this chain to let L2 know(token decimals maybe different on different chains)
    event NewToken(uint16 indexed tokenId, address indexed token, uint8 decimals);

    /// @notice Governor changed
    event NewGovernor(address newGovernor);

    /// @notice Validator's status changed
    event ValidatorStatusUpdate(address indexed validatorAddress, bool isActive);

    /// @notice Token pause status update
    event TokenPausedUpdate(uint16 indexed token, bool paused);

    /// @notice Sync service changed
    event SetSyncService(uint8 chainId, address newSyncService);

    /// @notice Gateway address changed
    event SetGateway(address indexed newGateway);

    /// @notice Event emitted when send sync hash to master chain
    event SendSlaverSyncHash(bytes32 syncHash);

    /// @notice Event emitted when send sync hash to arbitration
    event SendMasterSyncHash(uint32 blockNumber, bytes32 syncHash);

    /// @notice Event emitted when receive sync hash from a slaver chain
    event ReceiveSlaverSyncHash(uint8 slaverChainId, bytes32 syncHash);
}

/// @title Upgrade events
/// @author Matter Labs
interface UpgradeEvents {
    /// @notice Event emitted when new upgradeable contract is added to upgrade gatekeeper's list of managed contracts
    event NewUpgradable(uint256 indexed versionId, address indexed upgradeable);

    /// @notice Upgrade mode enter event
    event NoticePeriodStart(
        uint256 indexed versionId,
        address[] newTargets,
        uint256 noticePeriod // notice period (in seconds)
    );

    /// @notice Upgrade mode cancel event
    event UpgradeCancel(uint256 indexed versionId);

    /// @notice Upgrade mode complete event
    event UpgradeComplete(uint256 indexed versionId, address[] newTargets);
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



import "./Bytes.sol";
import "./Utils.sol";

/// @title zkSync operations tools
/// @dev Circuit ops and their pubdata (chunks * bytes)
library Operations {
    /// @dev zkSync circuit operation type
    enum OpType {
        Noop, // 0
        Deposit, // 1 L1 Op
        TransferToNew, // 2 L2 Op
        Withdraw, // 3 L2 Op
        Transfer, // 4 L2 Op
        FullExit, // 5 L1 Op
        ChangePubKey, // 6 L2 Op
        ForcedExit, // 7 L2 Op
        OrderMatching, // 8 L2 Op
        ContractMatching, // 9 L2 Op
        Liquidation, // 10 L2 Op
        AutoDeleveraging, // 11 L2 Op
        UpdateGlobalVar, // 12 L2 Op
        Funding // 13 L2 Op
    }

    // Byte lengths

    /// @dev op is uint8
    uint8 internal constant OP_TYPE_BYTES = 1;

    /// @dev chainId is uint8
    uint8 internal constant CHAIN_BYTES = 1;

    /// @dev token is uint16
    uint8 internal constant TOKEN_BYTES = 2;

    /// @dev nonce is uint32
    uint8 internal constant NONCE_BYTES = 4;

    /// @dev address is 20 bytes length
    uint8 internal constant ADDRESS_BYTES = 20;

    /// @dev address prefix zero bytes length
    uint8 internal constant ADDRESS_PREFIX_ZERO_BYTES = 12;

    /// @dev fee is uint16
    uint8 internal constant FEE_BYTES = 2;

    /// @dev accountId is uint32
    uint8 internal constant ACCOUNT_ID_BYTES = 4;

    /// @dev subAccountId is uint8
    uint8 internal constant SUB_ACCOUNT_ID_BYTES = 1;

    /// @dev amount is uint128
    uint8 internal constant AMOUNT_BYTES = 16;

    /// @dev Priority hash bytes length
    uint256 internal constant DEPOSIT_CHECK_BYTES = 55;
    uint256 internal constant FULL_EXIT_CHECK_BYTES = 43;

    // Priority operations: Deposit, FullExit
    struct PriorityOperation {
        bytes20 hashedPubData; // hashed priority operation public data
        uint64 expirationBlock; // expiration block number (ETH block) for this request (must be satisfied before)
        OpType opType; // priority operation type
    }

    struct Deposit {
        // uint8 opType
        uint8 chainId; // deposit from which chain that identified by L2 chain id
        uint8 subAccountId; // the sub account is bound to account, default value is 0(the global public sub account)
        uint16 tokenId; // the token that registered to L2
        uint16 targetTokenId; // the token that user increased in L2
        uint128 amount; // the token amount deposited to L2
        bytes32 owner; // the address that receive deposited token at L2
        uint32 accountId; // the account id bound to the owner address, ignored at serialization and will be set when the block is submitted
    } // 59 bytes

    /// @dev Deserialize deposit pubdata
    function readDepositPubdata(bytes memory _data) internal pure returns (Deposit memory parsed) {
        // NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
        uint256 offset = OP_TYPE_BYTES;
        (offset, parsed.chainId) = Bytes.readUint8(_data, offset);
        (offset, parsed.subAccountId) = Bytes.readUint8(_data, offset);
        (offset, parsed.tokenId) = Bytes.readUInt16(_data, offset);
        (offset, parsed.targetTokenId) = Bytes.readUInt16(_data, offset);
        (offset, parsed.amount) = Bytes.readUInt128(_data, offset);
        (offset, parsed.owner) = Bytes.readBytes32(_data, offset);
        (offset, parsed.accountId) = Bytes.readUInt32(_data, offset);
    }

    /// @dev Serialize deposit pubdata
    function writeDepositPubdataForPriorityQueue(Deposit memory op) internal pure returns (bytes memory buf) {
        buf = abi.encodePacked(
            uint8(OpType.Deposit),
            op.chainId,
            op.subAccountId,
            op.tokenId,
            op.targetTokenId,
            op.amount,
            op.owner,
            uint32(0) // accountId (ignored during hash calculation)
        );
    }

    /// @dev Checks that op pubdata is same as operation in priority queue
    function checkDepositOperation(bytes memory _opPubData, bytes20 hashedPubData) internal pure {
        require(Utils.hashBytesWithSizeToBytes20(_opPubData, DEPOSIT_CHECK_BYTES) == hashedPubData, "OP: invalid deposit hash");
    }

    struct FullExit {
        // uint8 opType
        uint8 chainId; // withdraw to which chain that identified by L2 chain id
        uint32 accountId; // the account id to withdraw from
        uint8 subAccountId; // the sub account is bound to account, default value is 0(the global public sub account)
        //bytes12 addressPrefixZero; -- address bytes length in L2 is 32
        address owner; // the address that own the account at L2
        uint16 tokenId; // the token that withdraw to L1
        uint16 srcTokenId; // the token that deducted in L2
        uint128 amount; // the token amount that fully withdrawn to owner, ignored at serialization and will be set when the block is submitted
    } // 59 bytes

    /// @dev Deserialize fullExit pubdata
    function readFullExitPubdata(bytes memory _data) internal pure returns (FullExit memory parsed) {
        // NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
        uint256 offset = OP_TYPE_BYTES;
        (offset, parsed.chainId) = Bytes.readUint8(_data, offset);
        (offset, parsed.accountId) = Bytes.readUInt32(_data, offset);
        (offset, parsed.subAccountId) = Bytes.readUint8(_data, offset);
        offset += ADDRESS_PREFIX_ZERO_BYTES;
        (offset, parsed.owner) = Bytes.readAddress(_data, offset);
        (offset, parsed.tokenId) = Bytes.readUInt16(_data, offset);
        (offset, parsed.srcTokenId) = Bytes.readUInt16(_data, offset);
        (offset, parsed.amount) = Bytes.readUInt128(_data, offset);
    }

    /// @dev Serialize fullExit pubdata
    function writeFullExitPubdataForPriorityQueue(FullExit memory op) internal pure returns (bytes memory buf) {
        buf = abi.encodePacked(
            uint8(OpType.FullExit),
            op.chainId,
            op.accountId,
            op.subAccountId,
            bytes12(0), // append 12 zero bytes
            op.owner,
            op.tokenId,
            op.srcTokenId,
            uint128(0) // amount(ignored during hash calculation)
        );
    }

    /// @dev Checks that op pubdata is same as operation in priority queue
    function checkFullExitOperation(bytes memory _opPubData, bytes20 hashedPubData) internal pure {
        require(Utils.hashBytesWithSizeToBytes20(_opPubData, FULL_EXIT_CHECK_BYTES) == hashedPubData, "OP: invalid fullExit hash");
    }

    struct Withdraw {
        //uint8 opType; -- present in pubdata, ignored at serialization
        uint8 chainId; // which chain the withdraw happened
        uint32 accountId; // the account id to withdraw from
        uint8 subAccountId; // the sub account id to withdraw from
        uint16 tokenId; // the token that to withdraw
        //uint16 srcTokenId; -- the token that decreased in L2, present in pubdata, ignored at serialization
        uint128 amount; // the token amount to withdraw
        //uint16 fee; -- present in pubdata, ignored at serialization
        //bytes12 addressPrefixZero; -- address bytes length in L2 is 32
        address owner; // the address to receive token
        uint32 nonce; // the sub account nonce
        uint16 fastWithdrawFeeRate; // fast withdraw fee rate taken by acceptor
        uint8 withdrawToL1; // when this flag is 1, it means withdraw token to L1
    } // 68 bytes

    function readWithdrawPubdata(bytes memory _data) internal pure returns (Withdraw memory parsed) {
        // NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
        uint256 offset = OP_TYPE_BYTES;
        (offset, parsed.chainId) = Bytes.readUint8(_data, offset);
        (offset, parsed.accountId) = Bytes.readUInt32(_data, offset);
        (offset, parsed.subAccountId) = Bytes.readUint8(_data, offset);
        (offset, parsed.tokenId) = Bytes.readUInt16(_data, offset);
        offset += TOKEN_BYTES;
        (offset, parsed.amount) = Bytes.readUInt128(_data, offset);
        offset += FEE_BYTES;
        offset += ADDRESS_PREFIX_ZERO_BYTES;
        (offset, parsed.owner) = Bytes.readAddress(_data, offset);
        (offset, parsed.nonce) = Bytes.readUInt32(_data, offset);
        (offset, parsed.fastWithdrawFeeRate) = Bytes.readUInt16(_data, offset);
        (offset, parsed.withdrawToL1) = Bytes.readUint8(_data, offset);
    }

    struct ForcedExit {
        //uint8 opType; -- present in pubdata, ignored at serialization
        uint8 chainId; // which chain the force exit happened
        uint32 initiatorAccountId; // the account id of initiator
        uint8 initiatorSubAccountId; // the sub account id of initiator
        uint32 initiatorNonce; // the sub account nonce of initiator
        uint32 targetAccountId; // the account id of target
        //uint8 targetSubAccountId; -- present in pubdata, ignored at serialization
        uint16 tokenId; // the token that to withdraw
        //uint16 srcTokenId; -- the token that decreased in L2, present in pubdata, ignored at serialization
        uint128 amount; // the token amount to withdraw
        uint8 withdrawToL1; // when this flag is 1, it means withdraw token to L1
        //bytes12 addressPrefixZero; -- address bytes length in L2 is 32
        address target; // the address to receive token
    } // 69 bytes

    function readForcedExitPubdata(bytes memory _data) internal pure returns (ForcedExit memory parsed) {
        // NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
        uint256 offset = OP_TYPE_BYTES;
        (offset, parsed.chainId) = Bytes.readUint8(_data, offset);
        (offset, parsed.initiatorAccountId) = Bytes.readUInt32(_data, offset);
        (offset, parsed.initiatorSubAccountId) = Bytes.readUint8(_data, offset);
        (offset, parsed.initiatorNonce) = Bytes.readUInt32(_data, offset);
        (offset, parsed.targetAccountId) = Bytes.readUInt32(_data, offset);
        offset += SUB_ACCOUNT_ID_BYTES;
        (offset, parsed.tokenId) = Bytes.readUInt16(_data, offset);
        offset += TOKEN_BYTES;
        (offset, parsed.amount) = Bytes.readUInt128(_data, offset);
        (offset, parsed.withdrawToL1) = Bytes.readUint8(_data, offset);
        offset += ADDRESS_PREFIX_ZERO_BYTES;
        (offset, parsed.target) = Bytes.readAddress(_data, offset);
    }

    // ChangePubKey
    struct ChangePubKey {
        // uint8 opType; -- present in pubdata, ignored at serialization
        uint8 chainId; // which chain to verify(only one chain need to verify for gas saving)
        uint32 accountId; // the account that to change pubkey
        //uint8 subAccountId; -- present in pubdata, ignored at serialization
        bytes20 pubKeyHash; // hash of the new rollup pubkey
        //bytes12 addressPrefixZero; -- address bytes length in L2 is 32
        address owner; // the owner that own this account
        uint32 nonce; // the account nonce
        //uint16 tokenId; -- present in pubdata, ignored at serialization
        //uint16 fee; -- present in pubdata, ignored at serialization
    } // 67 bytes

    function readChangePubKeyPubdata(bytes memory _data) internal pure returns (ChangePubKey memory parsed) {
        uint256 offset = OP_TYPE_BYTES;
        (offset, parsed.chainId) = Bytes.readUint8(_data, offset);
        (offset, parsed.accountId) = Bytes.readUInt32(_data, offset);
        offset += SUB_ACCOUNT_ID_BYTES;
        (offset, parsed.pubKeyHash) = Bytes.readBytes20(_data, offset);
        offset += ADDRESS_PREFIX_ZERO_BYTES;
        (offset, parsed.owner) = Bytes.readAddress(_data, offset);
        (offset, parsed.nonce) = Bytes.readUInt32(_data, offset);
    }
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 *
 * _Since v2.5.0:_ this module is now much more gas efficient, given net gas
 * metering changes introduced in the Istanbul hardfork.
 */
contract ReentrancyGuard {
    /// @dev Address of lock flag variable.
    /// @dev Flag is placed at random memory location to not interfere with Storage contract.
    uint256 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; // keccak256("ReentrancyGuard") - 1;

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

    function initializeReentrancyGuard() internal {
        uint256 lockSlotOldValue;

        // Storing an initial non-zero value makes deployment a bit more
        // expensive, but in exchange every call to nonReentrant
        // will be cheaper.
        assembly {
            lockSlotOldValue := sload(LOCK_FLAG_ADDRESS)
            sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
        }

        // Check that storage slot for reentrancy guard is empty to rule out possibility of double initialization
        require(lockSlotOldValue == 0, "1B");
    }

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

        // On the first call to nonReentrant, _notEntered will be true
        require(_status == _NOT_ENTERED);

        // Any calls to nonReentrant after this point will fail
        assembly {
            sstore(LOCK_FLAG_ADDRESS, _ENTERED)
        }

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        assembly {
            sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
        }
    }
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



/**
 * @dev Wrappers over Solidity's uintXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and then downcasting.
 *
 * _Available since v2.5.0._
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value < 2**128, "16");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value < 2**64, "17");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value < 2**32, "18");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value < 2**16, "19");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value < 2**8, "1a");
        return uint8(value);
    }
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



/// @title Interface of the upgradeable contract
/// @author Matter Labs
interface Upgradeable {
    /// @notice Upgrades target of upgradeable contract
    /// @param newTarget New target
    function upgradeTarget(address newTarget) external;
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



/// @title Interface of the upgradeable master contract (defines notice period duration and allows finish upgrade during preparation of it)
/// @author Matter Labs
interface UpgradeableMaster {
    /// @notice Notice period before activation preparation status of upgrade mode
    function getNoticePeriod() external returns (uint256);

    /// @notice Checks that contract is ready for upgrade
    /// @return bool flag indicating that contract is ready for upgrade
    function isReadyForUpgrade() external returns (bool);
}

pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



import "./Bytes.sol";

library Utils {
    /// @notice Returns lesser of two values
    function minU32(uint32 a, uint32 b) internal pure returns (uint32) {
        return a < b ? a : b;
    }

    /// @notice Returns lesser of two values
    function minU64(uint64 a, uint64 b) internal pure returns (uint64) {
        return a < b ? a : b;
    }

    /// @notice Returns lesser of two values
    function minU128(uint128 a, uint128 b) internal pure returns (uint128) {
        return a < b ? a : b;
    }

    /// @notice Recovers signer's address from ethereum signature for given message
    /// @param _signature 65 bytes concatenated. R (32) + S (32) + V (1)
    /// @param _messageHash signed message hash.
    /// @return address of the signer
    function recoverAddressFromEthSignature(bytes memory _signature, bytes32 _messageHash)
        internal
        pure
        returns (address)
    {
        require(_signature.length == 65, "ut0"); // incorrect signature length

        bytes32 signR;
        bytes32 signS;
        uint8 signV;
        assembly {
            signR := mload(add(_signature, 32))
            signS := mload(add(_signature, 64))
            signV := byte(0, mload(add(_signature, 96)))
        }

        return ecrecover(_messageHash, signV, signR, signS);
    }

    /// @notice Returns new_hash = hash(old_hash + bytes)
    function concatHash(bytes32 _hash, bytes memory _bytes) internal pure returns (bytes32) {
        bytes32 result;
        assembly {
            let bytesLen := add(mload(_bytes), 32)
            mstore(_bytes, _hash)
            result := keccak256(_bytes, bytesLen)
        }
        return result;
    }

    /// @notice Returns new_hash = hash(a + b)
    function concatTwoHash(bytes32 a, bytes32 b) internal pure returns (bytes32 value) {
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }

    function hashBytesWithSizeToBytes20(bytes memory _bytes, uint256 _size) internal pure returns (bytes20) {
        bytes32 result;
        assembly {
            result := keccak256(add(_bytes, 32), _size)
        }
        return bytes20(uint160(uint256(result)));
    }
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_periphery","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"acceptor","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"},{"indexed":false,"internalType":"uint16","name":"withdrawFeeRate","type":"uint16"},{"indexed":false,"internalType":"uint32","name":"accountIdOfNonce","type":"uint32"},{"indexed":false,"internalType":"uint8","name":"subAccountIdOfNonce","type":"uint8"},{"indexed":false,"internalType":"uint32","name":"nonce","type":"uint32"},{"indexed":false,"internalType":"uint128","name":"amountReceive","type":"uint128"}],"name":"Accept","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"blockNumber","type":"uint32"}],"name":"BlockCommit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"blockNumber","type":"uint32"}],"name":"BlockExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"blockNumber","type":"uint32"}],"name":"BlockProven","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"blockNumber","type":"uint32"}],"name":"BlockSynced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"totalBlocksVerified","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"totalBlocksCommitted","type":"uint32"}],"name":"BlocksRevert","type":"event"},{"anonymous":false,"inputs":[],"name":"ExodusMode","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint32","name":"nonce","type":"uint32"},{"indexed":false,"internalType":"bytes","name":"fact","type":"bytes"}],"name":"FactAuth","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint32","name":"nonce","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"FactAuthResetTime","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newGovernor","type":"address"}],"name":"NewGovernor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint64","name":"serialId","type":"uint64"},{"indexed":false,"internalType":"enum Operations.OpType","name":"opType","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"pubData","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"expirationBlock","type":"uint256"}],"name":"NewPriorityRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"NewToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"slaverChainId","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"syncHash","type":"bytes32"}],"name":"ReceiveSlaverSyncHash","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"blockNumber","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"syncHash","type":"bytes32"}],"name":"SendMasterSyncHash","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"syncHash","type":"bytes32"}],"name":"SendSlaverSyncHash","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newGateway","type":"address"}],"name":"SetGateway","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"chainId","type":"uint8"},{"indexed":false,"internalType":"address","name":"newSyncService","type":"address"}],"name":"SetSyncService","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"token","type":"uint16"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"TokenPausedUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"validatorAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"ValidatorStatusUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"Withdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"withdrawHash","type":"bytes32"}],"name":"WithdrawalL1","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":true,"internalType":"bytes32","name":"recepient","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"WithdrawalPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"withdrawHash","type":"bytes32"}],"name":"WithdrawalPendingL1","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"ALL_CHAINS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CHAIN_ID","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_FEE_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MASTER_CHAIN_ID","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_CHAIN_ID","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_CHAIN_ID","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint16","name":"fastWithdrawFeeRate","type":"uint16"},{"internalType":"uint32","name":"accountIdOfNonce","type":"uint32"},{"internalType":"uint8","name":"subAccountIdOfNonce","type":"uint8"},{"internalType":"uint32","name":"nonce","type":"uint32"}],"name":"acceptERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"receiver","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint16","name":"fastWithdrawFeeRate","type":"uint16"},{"internalType":"uint32","name":"accountIdOfNonce","type":"uint32"},{"internalType":"uint8","name":"subAccountIdOfNonce","type":"uint8"},{"internalType":"uint32","name":"nonce","type":"uint32"}],"name":"acceptETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"accepts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"authFacts","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"authFactsResetTimer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"chainSyncServiceMap","outputs":[{"internalType":"contract ISyncService","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint32","name":"blockSequence","type":"uint32"},{"internalType":"uint64","name":"priorityOperations","type":"uint64"},{"internalType":"bytes32","name":"pendingOnchainOperationsHash","type":"bytes32"},{"internalType":"bytes32","name":"syncHash","type":"bytes32"}],"internalType":"struct Storage.StoredBlockInfo","name":"_lastCommittedBlockData","type":"tuple"},{"components":[{"internalType":"bytes32","name":"newStateHash","type":"bytes32"},{"internalType":"bytes","name":"publicData","type":"bytes"},{"components":[{"internalType":"bytes","name":"ethWitness","type":"bytes"},{"internalType":"uint32","name":"publicDataOffset","type":"uint32"}],"internalType":"struct ZkLink.OnchainOperationData[]","name":"onchainOperations","type":"tuple[]"},{"internalType":"uint32","name":"blockNumber","type":"uint32"}],"internalType":"struct ZkLink.CommitBlockInfo[]","name":"_newBlocksData","type":"tuple[]"}],"name":"commitCompressedBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint32","name":"blockSequence","type":"uint32"},{"internalType":"uint64","name":"priorityOperations","type":"uint64"},{"internalType":"bytes32","name":"pendingOnchainOperationsHash","type":"bytes32"},{"internalType":"bytes32","name":"syncHash","type":"bytes32"}],"internalType":"struct Storage.StoredBlockInfo","name":"storedBlock","type":"tuple"},{"internalType":"bytes[]","name":"pendingOnchainOpsPubdata","type":"bytes[]"}],"internalType":"struct ZkLink.ExecuteBlockInfo[]","name":"_blocksData","type":"tuple[]"}],"name":"executeCompressedBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exodusMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"firstPriorityRequestId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gateway","outputs":[{"internalType":"contract IL2Gateway","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNoticePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"initializationParameters","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isReadyForUpgrade","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"networkGovernor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"pendingL1Withdraws","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"uint16","name":"","type":"uint16"},{"internalType":"uint16","name":"","type":"uint16"}],"name":"performedExodus","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periphery","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"priorityRequests","outputs":[{"internalType":"bytes20","name":"hashedPubData","type":"bytes20"},{"internalType":"uint64","name":"expirationBlock","type":"uint64"},{"internalType":"enum Operations.OpType","name":"opType","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"storedBlockHashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"syncServiceMap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"synchronizedChains","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenIds","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"","type":"uint16"}],"name":"tokens","outputs":[{"internalType":"bool","name":"registered","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBlocksCommitted","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBlocksExecuted","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBlocksProven","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBlocksSynchronized","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCommittedPriorityRequests","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalOpenPriorityRequests","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"validators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifier","outputs":[{"internalType":"contract IVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60c0346200008457601f62003c9238819003918201601f19168301916001600160401b0383118484101762000089578084926020946040528339810103126200008457516001600160a01b038116810362000084573060805260a052604051613bf29081620000a082396080518161083d015260a051818181610d6f01526115910152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361061158f5760003560e01c80630df8f6221461026c5780630e9aa83f1461026757806310a7132414610262578063116191b61461025d5780631914f44714610258578063264c0912146102535780632a3174f41461024e5780632b7ac3f31461024957806334f6bb1c14610244578063439fab911461023f578063495ed57a1461023a5780634d886d7f14610235578063591e3229146102305780635ac7ff261461022b5780635bc105c01461022657806364494fdf14610221578063647b59231461021c57806367708dae1461021757806377aace1a146102125780637d4907981461020d5780638279c74c1461020857806385e1f4d0146102035780638773334c146101fe5780638ae20dc9146101f95780639ba0d146146101f4578063b65975ec146101ef578063c241e5ca146101ea578063c3a5f891146101e5578063c57b22be146101e0578063dd98f3d8146101db578063e10136ff146101d6578063f2235487146101d1578063f39349ef146101cc578063f3c20de0146101c7578063fa52c7d8146101c2578063faf4d8cb146101bd578063fc97a303146101b85763fe9806b10361158f57611463565b611422565b6113fb565b6113b9565b611356565b61132d565b611306565b6112d7565b611295565b611274565b6110e7565b6110b6565b611062565b61102b565b610fce565b610faa565b610f8e565b610e30565b610dc3565b610d59565b610d38565b610d11565b610cf5565b610cd9565b610b17565b610a9e565b610a09565b610982565b6107eb565b6107ca565b6107a1565b610785565b610760565b61072c565b610703565b6106e7565b61050e565b3461028857600036600319011261028857602060405160018152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b038211176102be57604052565b61028d565b608081019081106001600160401b038211176102be57604052565b604081019081106001600160401b038211176102be57604052565b606081019081106001600160401b038211176102be57604052565b90601f801991011681019081106001600160401b038211176102be57604052565b60405190610342826102a3565b565b6004359063ffffffff8216820361028857565b6084359063ffffffff8216820361028857565b60c4359063ffffffff8216820361028857565b6024359063ffffffff8216820361028857565b6064359063ffffffff8216820361028857565b60a4359063ffffffff8216820361028857565b359063ffffffff8216820361028857565b60a090600319011261028857604051906103e0826102a3565b8163ffffffff600435818116810361028857825260243590811681036102885760208201526044356001600160401b038116810361028857604082015260643560608201526080608435910152565b91908260a091031261028857604051610447816102a3565b8092610452816103b6565b8252610460602082016103b6565b602083015260408101356001600160401b03811681036102885760809182916040850152606081013560608501520135910152565b6001600160401b0381116102be5760051b60200190565b6001600160401b0381116102be57601f01601f191660200190565b81601f82011215610288578035906104de826104ac565b926104ec6040519485610314565b8284526020838301011161028857816000926020809301838601378301015290565b346102885760c036600319011261028857610528366103c7565b60a435906001600160401b039081831161028857366023840112156102885782600401359261055684610495565b92604061056581519586610314565b8585526020958686019160249384600593841b8701019536871161028857858101945b87861061059b576105998a8a611637565b005b853583811161028857820190608060231983360301126102885784516105c0816102c3565b88830135815260449283810135868111610288576105e48f918c36918501016104c7565b908301526064938482013587811161028857820136604382011215610288578f9582828b948f8e95013561062361061a82610495565b97519788610314565b8087528a8701951b82010192368411610288579082918a999897969594938201945b84861061067357505050505050610663916084918a850152016103b6565b6060820152815201950194610588565b909192939480969798999a5035908d8211610288578e918401916043198336030112610288578d908f516106a6816102de565b86840135928311610288576106cf858f956106c787968b36918401016104c7565b8452016103b6565b838201528152019501939291908a9998979695610645565b3461028857600036600319011261028857602060405160f08152f35b34610288576000366003190112610288576034546040516001600160a01b039091168152602090f35b34610288576020366003190112610288576004356000526000602052602060018060a01b0360406000205416604051908152f35b3461028857600036600319011261028857602060355460ff60405191831c1615158152f35b3461028857600036600319011261028857602060405160008152f35b34610288576000366003190112610288576032546040516001600160a01b039091168152602090f35b3461028857600036600319011261028857602060345460c01c604051908152f35b34610288576020366003190112610288576004356001600160401b038082116102885736602383011215610288578160040135908111610288578101602401368111610288576001600160a01b0390307f000000000000000000000000000000000000000000000000000000000000000083161461095957816108b96108826108d693602461092c9761087c613adf565b01611497565b951692166108918115156114cb565b61089c8315156114fc565b60018060a01b03166001600160601b0360a01b6032541617603255565b60018060a01b03166001600160601b0360a01b6033541617603355565b6108ed6108e1610335565b63ffffffff9092168252565b60006020820152600060408201527fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470806060830152608082015261152d565b60008052603c6020527f33d13c149959174817b07214f6faff3d4c1d39ff89c6e7f82e2df5f04c00a0ec55005b60405162461bcd60e51b81526020600482015260016024820152601960f91b6044820152606490fd5b3461028857600036600319011261028857602063ffffffff60355416604051908152f35b6004359060ff8216820361028857565b60a4359060ff8216820361028857565b6084359060ff8216820361028857565b6044359061ffff8216820361028857565b6064359061ffff8216820361028857565b6004359061ffff8216820361028857565b3461028857608036600319011261028857610a22610344565b6024359060ff821680920361028857610a9260ff91610a7f602094610a456109d6565b9063ffffffff610a536109e7565b941660005260388752604060002090600052865260406000209061ffff16600052602052604060002090565b9061ffff16600052602052604060002090565b54166040519015158152f35b346102885760203660031901126102885760ff610ab96109a6565b166000526041602052602060018060a01b0360406000205416604051908152f35b6001600160a01b0381160361028857565b604435906001600160801b038216820361028857565b602435906001600160801b038216820361028857565b346102885760e0366003190112610288577f05bb9b3f234e9dac83713761242d40320705b4204ba263981970118574d999cb600435610b5581610ada565b60243590610b6282610ada565b610cba610b6d610aeb565b610b756109e7565b610b7d610357565b610b856109b6565b90610b8e61036a565b92610b9f84848484898d8d33613182565b94610c4e610c3a60018060a01b03808c166000808d6040519460208601916323b872dd60e01b83523360248801521660448601526001600160801b038d16606486015260648552610bef856102a3565b60405194610bfc866102de565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af1610c34612e63565b91612f87565b8051908115918215610cbf575b5050612edc565b604080513381526001600160a01b03998a16602082015299909816978901979097526001600160801b03968716606089015261ffff16608088015263ffffffff90811660a088015260ff9190911660c08701521660e08501529091166101008301528190610120820190565b0390a1005b610cd29250602080918301019101612ec4565b3880610c47565b3461028857600036600319011261028857602060405160088152f35b3461028857600036600319011261028857602060405160078152f35b3461028857600036600319011261028857602063ffffffff60345460a01c16604051908152f35b3461028857600036600319011261028857602060325460c01c604051908152f35b34610288576000366003190112610288576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052602160045260246000fd5b600e1115610dbe57565b610d9e565b34610288576020366003190112610288576004356001600160401b039081811680910361028857600052603960205260406000205460ff8160e01c1691604051916001600160601b03198160601b16835260a01c166020820152600e821015610dbe576060916040820152f35b3461028857602080600319360112610288576001600160401b03600435818111610288573660238201121561028857806004013591610e6e83610495565b92604090610e7e82519586610314565b8085528585019160249384600593841b8701019536871161028857858101945b878610610eae5761059989612035565b85358381116102885782019060c0602319833603011261028857845191610ed4836102de565b610ee0368a830161042f565b835260c481013590858211610288570191366043840112156102885788830135610f0981610495565b90610f1688519283610314565b8082528d820190604480918b1b8701019036821161028857908f9691828189989796959401935b838510610f5857505050505083820152815201950194610e9e565b90919293809596979850358b8111610288578991610f7c83928636918701016104c7565b81520194019291908897969594610f3d565b3461028857600036600319011261028857602060405160068152f35b3461028857600036600319011261028857602060355460ff60405191831c16158152f35b34610288576040366003190112610288576020611022600435610ff081610ada565b610ff861037d565b9060018060a01b0316600052603a835260406000209063ffffffff16600052602052604060002090565b54604051908152f35b346102885760203660031901126102885763ffffffff611049610344565b16600052603c6020526020604060002054604051908152f35b3461028857604036600319011261028857602061102260043561108481610ada565b61108c61037d565b9060018060a01b0316600052603b835260406000209063ffffffff16600052602052604060002090565b34610288576020366003190112610288576004356000526037602052602060ff604060002054166040519015158152f35b60c0366003190112610288576004356110ff81610ada565b611107610b01565b61110f6109d6565b90611118610390565b926111216109c6565b6111296103a3565b6001600160a01b0383169190611144818389898988336130bd565b916001600160801b038316978834039734891161126f57899560008080809d81945af161116f612e63565b5015611245577f05bb9b3f234e9dac83713761242d40320705b4204ba263981970118574d999cb988a611220973403611226575b5050604080513381526001600160a01b03909816602089015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee908801526001600160801b03978816606088015261ffff16608087015263ffffffff90811660a087015260ff90911660c08601521660e084015292909216610100820152908190610120820190565b0390a180f35b80808061123e94335af1611238612e63565b50612e93565b388a6111a3565b60405162461bcd60e51b8152602060048201526002602482015261045360f41b6044820152606490fd5b6118ff565b3461028857600036600319011261028857602060335460c01c604051908152f35b34610288576020366003190112610288576004356112b281610ada565b60018060a01b03166000526042602052602060ff604060002054166040519015158152f35b3461028857600036600319011261028857602060405173374632e7d48b7872d904524fdc5dd4516f42cdff8152f35b3461028857600036600319011261028857602063ffffffff60325460a01c16604051908152f35b34610288576000366003190112610288576033546040516001600160a01b039091168152602090f35b346102885760203660031901126102885761ffff6113726109f8565b16600052603f602052608060406000205460ff6040519181811615158352818160081c161515602084015260018060a01b038160101c16604084015260b01c166060820152f35b34610288576020366003190112610288576004356113d681610ada565b60018060a01b0316600052603e602052602060ff604060002054166040519015158152f35b3461028857600036600319011261028857602063ffffffff60335460a01c16604051908152f35b346102885760203660031901126102885760043561143f81610ada565b60018060a01b03166000526040602052602061ffff60406000205416604051908152f35b346102885760203660031901126102885760ff61147e6109a6565b16600052603d6020526020604060002054604051908152f35b908160609103126102885780356114ad81610ada565b916114c8604060208401356114c181610ada565b93016103b6565b90565b156114d257565b60405162461bcd60e51b8152602060048201526002602482015261069360f41b6044820152606490fd5b1561150357565b60405162461bcd60e51b8152602060048201526002602482015261349960f11b6044820152606490fd5b604051602081019163ffffffff808251168452602082015116604083015260806001600160401b0391826040820151166060850152606081015182850152015160a083015260a0825260c0820190828210908211176102be5760405251902090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038116156115de576000808092368280378136915af43d82803e156115da573d90f35b3d90fd5b60405162461bcd60e51b81526020600482015260016024820152603560f81b6044820152606490fd5b1561160e57565b60405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606490fd5b9061164a60ff60355460201c1615611607565b6000338152603e60205260409161166660ff848420541661186d565b6001937f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf49385855403611869578580916002875583946116a88151151561189d565b6033546116e39060a01c63ffffffff16956116dd6116d68863ffffffff16600052603c602052604060002090565b549161152d565b146118ce565b86935b6117aa575b505050506117a7929161175b6117659261173661170a60345460c01c90565b6001600160401b0361172d61172160335460c01c90565b6001600160401b031690565b91161115611974565b6033805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b5163ffffffff1690565b907f81a92942d0f9c33b897a438384c9c3d88be397776138efa3ba1a4fc8b626842463ffffffff83169180a263ffffffff1663ffffffff196035541617603555565b55565b90919294815163ffffffff87169081101561185f57611856916117d06117d79285611940565b51906119fd565b956118236117fe6117ea60345460c01c90565b898701516001600160401b03165b90611959565b603480546001600160c01b031660c09290921b6001600160c01b031916919091179055565b61183561182f8861152d565b96611915565b956118508763ffffffff16600052603c602052604060002090565b55611915565b929190826116e6565b50948392506116eb565b8380fd5b1561187457565b60405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606490fd5b156118a457565b60405162461bcd60e51b8152602060048201526002602482015261066360f41b6044820152606490fd5b156118d557565b60405162461bcd60e51b8152602060048201526002602482015261663160f01b6044820152606490fd5b634e487b7160e01b600052601160045260246000fd5b63ffffffff80911690811461126f5760010190565b634e487b7160e01b600052603260045260246000fd5b80518210156119545760209160051b010190565b61192a565b9190916001600160401b038080941691160191821161126f57565b1561197b57565b60405162461bcd60e51b8152602060048201526002602482015261331960f11b6044820152606490fd5b604051906119b2826102a3565b60006080838281528260208201528260408201528260608201520152565b90600163ffffffff8093160191821161126f57565b91909163ffffffff8080941691160191821161126f57565b90611a066119a5565b50606081019063ffffffff80835116908451161015611ab957611aac611a5c611a9c611a78611a736020611a67611a3c88611bdc565b9097919960808d015190611a548d5163ffffffff1690565b905191612347565b985163ffffffff1690565b98015163ffffffff1690565b6119d0565b611a8f611a83610335565b63ffffffff9098168852565b63ffffffff166020870152565b6001600160401b03166040850152565b6060830152608082015290565b60405162461bcd60e51b8152602060048201526002602482015261067360f41b6044820152606490fd5b15611aea57565b60405162461bcd60e51b8152602060048201526002602482015261068360f41b6044820152606490fd5b600019811461126f5760010190565b906001820180921161126f57565b906002820180921161126f57565b90600c820180921161126f57565b15611b5457565b60405162461bcd60e51b8152602060048201526002602482015261683160f01b6044820152606490fd5b15611b8557565b60405162461bcd60e51b8152602060048201526002602482015261341960f11b6044820152606490fd5b8051156119545760200190565b908151811015611954570160200190565b60ff16600e811015610dbe5790565b60209081810151611bf8611bf282516017900690565b15611ae3565b611c12611c0760325460c01c90565b60345460c01c6117f8565b6000937fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47092839660005b60408701518051821015611d3457611ce8969798611cdf611c6084611cf694611940565b51611c7d611c748983015163ffffffff1690565b63ffffffff1690565b90611c92611c8a83611b23565b895111611b4d565b611c9f6017830615611b7e565b611ccb611cc6611cc0611cb2858c611bbc565b516001600160f81b03191690565b60f81c90565b611bcd565b9188611cd7868d611959565b925193611e15565b99919092611959565b989060208151019181522090565b958051611d14575b50611d0a604091611b14565b9050969596611c3c565b611d2c611d0a916040939b9060208151019181522090565b999150611cfe565b5050939450505050565b604051602081018181106001600160401b038211176102be5760405260008152906000368137565b60405190611d73826102c3565b604582526060366020840137565b90611d8b826104ac565b611d986040519182610314565b8281528092611da9601f19916104ac565b0190602036910137565b15611dba57565b60405162461bcd60e51b81526020600482015260026024820152616b3160f01b6044820152606490fd5b15611deb57565b60405162461bcd60e51b815260206004820152600260248201526106b360f41b6044820152606490fd5b9293600090611e22611d3e565b94611e2c81610db4565b60018103611e7657505050611e64611e4a611e7193611e6b9361340c565b946001600160401b03166000526039602052604060002090565b5460601b90565b83613541565b600192565b611e87819895949398979297610db4565b60068103611f6857505090611e9b9161340c565b92611ea584613a0d565b815190919015611ec15761034291611ebc9161238e565b611de4565b5080611f2e6040611f1d611efe611ee4606061034297015160018060a01b031690565b6001600160a01b03166000908152603a6020526040902090565b608085015163ffffffff1663ffffffff16600052602052604060002090565b549201516001600160601b03191690565b604051611f5f81611f516020820194856014916001600160601b03191681520190565b03601f198101835282610314565b51902014611db3565b93955091935091611f7881610db4565b60038103611f9957505090611f8c9161340c565b905b6114c882518361339b565b611fa281610db4565b60078103611fbc57505090611fb69161340c565b90611f8e565b600591949550611fcb81610db4565b0361200b57611e64611fe361200393611ffd9361340c565b936001600160401b03166000526039602052604060002090565b8261378b565b600191611f8e565b60405162461bcd60e51b8152602060048201526002602482015261359960f11b6044820152606490fd5b6035549060209161204b60ff82851c1615611607565b6000338152603e8452604061206560ff828420541661186d565b6001947f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf49486865403611869579186806120d3926120d89460028a556120af875163ffffffff1690565b966120f5611c74896120ea6120de63ffffffff9b8c9484868096169d8e1515612285565b6122b6565b86611940565b51515163ffffffff1690565b169b168b11156122cc565b60325460a01c63ffffffff169689968a965b6121f8575b505050505050507f036cca5fc51c8ae4669768ca5966397d55516afb697541fdd4fe7e6b253c075c93926121f3926121c96121a48461218161215c6121ce9761215760325460c01c90565b611959565b603280546001600160c01b031660c09290921b6001600160c01b031916919091179055565b6121996117fe8261219460345460c01c90565b61232e565b60335460c01c61232e565b603380546001600160c01b031660c09290921b6001600160c01b031916919091179055565b6119e5565b6032805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b80a255565b909192939495968288168281101561227b5761226f916117f8886122699361225a6122328f8f611a739061222b926119e5565b928b611940565b51916122546122498d85510163ffffffff90511690565b8b16828c16146122fd565b8261292e565b5101516001600160401b031690565b97611915565b95949392919085612107565b509686955061210c565b1561228c57565b60405162461bcd60e51b8152602060048201526002602482015261064360f41b6044820152606490fd5b63ffffffff908116600019019190821161126f57565b156122d357565b60405162461bcd60e51b8152602060048201526002602482015261643160f01b6044820152606490fd5b1561230457565b60405162461bcd60e51b8152602060048201526002602482015261321960f11b6044820152606490fd5b6001600160401b03918216908216039190821161126f57565b929091604051926020840194855263ffffffff60e01b9060e01b166040840152604483015260648201526064815261237e816102a3565b51902090565b60021115610dbe57565b80511561195457602081015160f81c6002811015610dbe576123af81612384565b806123bf5750906114c89161252c565b806123cb600192612384565b036124eb576124a26123f4826124936124326123fa6123ec6124ae976134a3565b9590856134fb565b946134fb565b6040890151909491506001600160601b03191660408051602081019384526001600160601b0319909216908201529182906054820190565b0391612446601f1993848101835282610314565b5190206040516001600160f81b03196020820190815260609690961b6001600160601b03191660218201526035810191909152605581019390935282607581015b03908101835282610314565b5190206001600160a01b031690565b6001600160a01b031690565b60608201516124c5906001600160a01b03166124a2565b6001600160a01b039091161490816124db575090565b6080015163ffffffff1615919050565b5050600090565b60005b8381106125055750506000910152565b81810151838201526020016124f5565b90612528602092828151948592016124f2565b0190565b6124a2606061267f61254061268e94613455565b90506125f261248761267661257461256f6124a261256960408d01516001600160601b03191690565b60601c90565b612838565b61261c8a6125ec6126056125b16125a0611c7460206125a56125a0611c7460808a015163ffffffff1690565b61269d565b96015163ffffffff1690565b926125ec604051998a976125ec60208a016019907f4368616e67655075624b65790a5075624b6579486173683a200000000000000081520190565b90612515565b6652737b731b29d160c51b815260080190565b6a520b1b1b7bab73a24b21d160a51b8152600c0190565b0361262f601f1991828101865285610314565b612639845161269d565b936040519384916125ec60208401976125ec89601a907f19457468657265756d205369676e6564204d6573736167653a0a00000000000081520190565b51902090613b42565b9301516001600160a01b031690565b6001600160a01b039091161490565b806000917a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000808210156127d2575b506d04ee2d6d415b85acef8100000000808310156127c3575b50662386f26fc10000808310156127b4575b506305f5e100808310156127a5575b5061271080831015612796575b506064821015612786575b600a8092101561277c575b600190816021612734828701611d81565b95860101905b612746575b5050505090565b600019019083906f181899199a1a9b1b9c1cb0b131b232b360811b8282061a8353049182156127775791908261273a565b61273f565b9160010191612723565b9190606460029104910191612718565b6004919392049101913861270d565b60089193920491019138612700565b601091939204910191386126f1565b602091939204910191386126df565b6040935081049150386126c6565b801561126f576000190190565b156127f457565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b60405190612845826102f9565b602a82526040366020840137603061285c83611baf565b5381516001908110156119545790607860218401536029915b80831161288857506114c89150156127ed565b90600f8116906010821015611954576128c5916f181899199a1a9b1b9c1cb0b131b232b360811b901a6128bb8587611bbc565b5360041c926127e0565b9190612875565b156128d357565b60405162461bcd60e51b815260206004820152600260248201526106d360f41b6044820152606490fd5b1561290457565b60405162461bcd60e51b81526020600482015260026024820152616d3360f01b6044820152606490fd5b9061295e90612957612940845161152d565b9163ffffffff16600052603c602052604060002090565b54146128cc565b60007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4705b6020908184015180519063ffffffff851691821015612b8257906129a591611940565b51916129b9611cc6611cc0611cb286611baf565b6129c281610db4565b60038103612a74575091612a6891612a5b612a6e946129f06129e385613846565b9182015163ffffffff1690565b906129ff604082015160ff1690565b60c082015163ffffffff1660a08301516001600160a01b0316606084015161ffff1691612a3660808601516001600160801b031690565b93612a55610100612a4c60e089015161ffff1690565b97015160ff1690565b96612ce2565b9060208151019181522090565b91611915565b90612982565b612a7d81610db4565b60078103612b05575091612a6891612b00612a6e94612a9e6129e385613920565b90612aad604082015160ff1690565b606082015163ffffffff166101008301516001600160a01b031690612ad760a085015161ffff1690565b92612afa60e0612af160c08801516001600160801b031690565b96015160ff1690565b95612bc7565b612a5b565b60059150612b1281610db4565b03612b585781612a6891612b00612b2b612a6e956135a6565b6080810151606082015160c0909201516001600160801b0316916001600160a01b03169061ffff16612db1565b60405162461bcd60e51b8152602060048201526002602482015261369960f11b6044820152606490fd5b5050915050606061034292510151146128fd565b15612b9d57565b60405162461bcd60e51b815260206004820152600260248201526106f360f41b6044820152606490fd5b612c1560019260ff9296959661ffff8716600052603f6020528760406000205496612bf3868916612b96565b612c02868960b01c168c612d31565b93878060a01b03809960101c16936131ec565b961603612c6b5750505050612c44612c37826000526037602052604060002090565b805460ff19166001179055565b7f2697a58ffa01960ced18161304752c663ba354619840bdf68d763b1b88c09781600080a2565b612c8f612c82866000526000602052604060002090565b546001600160a01b031690565b908116612cd65750612cd182612cb2610342966000526000602052604060002090565b80546001600160a01b0319166001600160a01b03909216919091179055565b612db1565b91506103429350612db1565b60019293959660ff92612c159261ffff8816600052603f6020528860406000205497612d0f878a16612b96565b612d1e878a60b01c168d612d31565b93888060a01b03809a60101c169361330d565b9060ff1660120360ff811161126f5760ff16604d811161126f57600a0a600160801b811015612d87576001600160801b03809116918215612d7157160490565b634e487b7160e01b600052601260045260246000fd5b60405162461bcd60e51b8152602060048201526002602482015261189b60f11b6044820152606490fd5b919060018060a01b0316918260005260366020526001600160801b038080612deb8460406000209061ffff16600052602052604060002090565b541693168093019080821161126f577f3cfb74f0f066330f203d8ac39c3fef52fc056de4d011fc7d91dadd9ba69834169260209261ffff928760005260368552612e478360406000209061ffff16600052602052604060002090565b91166001600160801b03198254161790556040519485521692a3565b3d15612e8e573d90612e74826104ac565b91612e826040519384610314565b82523d6000602084013e565b606090565b15612e9a57565b60405162461bcd60e51b8152602060048201526002602482015261453160f01b6044820152606490fd5b90816020910312610288575180151581036102885790565b15612ee357565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b15612f4257565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b91929015612fa75750815115612f9b575090565b6114c8903b1515612f3b565b825190915015612fba5750805190602001fd5b6044604051809262461bcd60e51b825260206004830152612fea81518092816024860152602086860191016124f2565b601f01601f19168101030190fd5b15612fff57565b60405162461bcd60e51b8152602060048201526002602482015261241960f11b6044820152606490fd5b1561303057565b60405162461bcd60e51b8152602060048201526002602482015261483360f01b6044820152606490fd5b9061ffff8092166127100391821161126f57565b9190916001600160801b038080941691160291821691820361126f57565b1561309357565b60405162461bcd60e51b8152602060048201526002602482015261120d60f21b6044820152606490fd5b9396956001600160a01b03938483169390929091841561315857610342976130ef61312596612cb2988a161415612ff8565b6127106001600160801b0361311d61ffff61310d84828b1610613029565b6131168961305a565b168761306e565b16049a613267565b6131486131426124a2612c82846000526000602052604060002090565b1561308c565b6000526000602052604060002090565b60405162461bcd60e51b8152602060048201526002602482015261483160f01b6044820152606490fd5b9497966001600160a01b039485831694909390929091851561315857610342986131b661312597612cb2998b161415612ff8565b6127106001600160801b036131e461ffff6131d484828c1610613029565b6131dd8a61305a565b168861306e565b16049b61330d565b94929193909460405194602086019663ffffffff60e01b809360e01b16885260ff60f81b9060f81b16602487015260e01b1660258501526001600160601b0319809260601b16602985015260601b16603d8301526001600160801b03199060801b166051820152600060618201526043815261237e816102c3565b60405160e091821b6001600160e01b03199081166020830190815260f89490941b6001600160f81b03191660248301529390911b909216602583015260609290921b6001600160601b031916602982015273777777777777777777777777777777777777777760611b603d82015260809290921b6001600160801b031916605183015260f09290921b6001600160f01b03191660618201525b6043815261237e816102c3565b60405160e091821b6001600160e01b03199081166020830190815260f89490941b6001600160f81b03191660248301529390911b9092166025830152606092831b6001600160601b031990811660298401529390921b909216603d83015260809290921b6001600160801b031916605182015260f09290921b6001600160f01b031916606183015290613300565b90808251106133e3576133ad81611d81565b91816133b857505090565b602091830182019082018383015b8281106133d4575050505090565b815181529083019083016133c6565b60405162461bcd60e51b81526020600482015260016024820152602d60f91b6044820152606490fd5b908151604582019081831161126f57106133e357613428611d66565b9160209160658401910182018284015b828110613446575050505090565b81518152908301908301613438565b60428151106133e3576040519061346b826102c3565b6041825260209081830160603682376021606185019201905b8281106134945750505050604291565b81518152908301908301613484565b90601591828151106134b55782015190565b60405162461bcd60e51b81526020600482015260016024820152602960f91b6044820152606490fd5b60148201929183811161126f57838251106134b557016014015190565b60208201929183811161126f578382511061351857016020015190565b60405162461bcd60e51b81526020600482015260016024820152605960f81b6044820152606490fd5b60209060376001600160601b0319928392012060601b1691160361356157565b60405162461bcd60e51b815260206004820152601860248201527f4f503a20696e76616c6964206465706f736974206861736800000000000000006044820152606490fd5b906040519160e083018381106001600160401b038211176102be57604052600090818452602084018281526040850183815260608601848152608087019185835260a088019386855260c089019687528861360087613682565b60ff1690915261361090876136ff565b63ffffffff16909152613623908661369a565b60ff1690915261363290611b3f565b61363c90856134de565b6001600160a01b031690915261365290846136b9565b61ffff1690915261366390836136b9565b61ffff1690915261367391613745565b6001600160801b031690915250565b90600291805160011015611954576021015160f81c90565b9190600181019081811161126f576136b29193611bbc565b5160f81c90565b60028201929183811161126f57838251106136d657016002015190565b60405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606490fd5b60048201929183811161126f578382511061371c57016004015190565b60405162461bcd60e51b81526020600482015260016024820152602b60f91b6044820152606490fd5b60108201929183811161126f578382511061376257016010015190565b60405162461bcd60e51b81526020600482015260016024820152605760f81b6044820152606490fd5b602090602b6001600160601b0319928392012060601b169116036137ab57565b60405162461bcd60e51b815260206004820152601960248201527f4f503a20696e76616c69642066756c6c457869742068617368000000000000006044820152606490fd5b6040519061012082018281106001600160401b038211176102be57604052816101006000918281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201520152565b9061384f6137f0565b9161385981613682565b60ff16845261386890826136ff565b63ffffffff16602085015261387d908261369a565b60ff16604085015261388f90826136b9565b61ffff1660608501526138a190611b31565b6138ab9082613745565b6001600160801b031660808501526138c290611b31565b6138cb90611b3f565b6138d590826134de565b6001600160a01b031660a08501526138ed90826136ff565b63ffffffff1660c085015261390290826136b9565b61ffff1660e08501526139149161369a565b60ff1661010084015250565b906103426139fb836139f56139f06139e56139df6139ce6139c861ffff6139ba6139b461394b6137f0565b9d8e60806139a861396c60ff61396086613682565b919091168552856136ff565b909461399a60ff61398b63ffffffff988980961660208a01528461369a565b919091166040880152826136ff565b9290921660608601526136ff565b93909316910152611b23565b896136b9565b9190911660a08d0152611b31565b86613745565b6001600160801b031660c08b015290565b8461369a565b60ff1660e089015290565b611b3f565b906134de565b6001600160a01b031661010085015250565b90613a166119a5565b9163ffffffff613a3660ff613a2a84613682565b919091168652836136ff565b919091166020850152600181019081811161126f576015810180921161126f5781835110613ab65782613a92613a8c613aa8946139f06021613aa3966103429901015160408b01906001600160601b0319169052565b826134de565b6001600160a01b0316606088015291565b6136ff565b63ffffffff16608085015250565b60405162461bcd60e51b81526020600482015260016024820152605360f81b6044820152606490fd5b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4600181549155613b0c57565b60405162461bcd60e51b815260206004820152600260248201526118a160f11b6044820152606490fd5b6040513d6000823e3d90fd5b6041815103613b91576020818101516040808401516060948501518251968752600090811a8786015291860192909252928401528180529160809060015afa15613b8c5760005190565b613b36565b60405162461bcd60e51b815260206004820152600360248201526207574360ec1b6044820152606490fdfea2646970667358221220d8b3407da7d8cc03d7000b15cbddf506338e72da9ef707ab362a0c896392a15c64736f6c63430008120033000000000000000000000000413552461b0b2c13f117d885b52aaa2f23374b1d

Deployed Bytecode

0x60806040526004361061158f5760003560e01c80630df8f6221461026c5780630e9aa83f1461026757806310a7132414610262578063116191b61461025d5780631914f44714610258578063264c0912146102535780632a3174f41461024e5780632b7ac3f31461024957806334f6bb1c14610244578063439fab911461023f578063495ed57a1461023a5780634d886d7f14610235578063591e3229146102305780635ac7ff261461022b5780635bc105c01461022657806364494fdf14610221578063647b59231461021c57806367708dae1461021757806377aace1a146102125780637d4907981461020d5780638279c74c1461020857806385e1f4d0146102035780638773334c146101fe5780638ae20dc9146101f95780639ba0d146146101f4578063b65975ec146101ef578063c241e5ca146101ea578063c3a5f891146101e5578063c57b22be146101e0578063dd98f3d8146101db578063e10136ff146101d6578063f2235487146101d1578063f39349ef146101cc578063f3c20de0146101c7578063fa52c7d8146101c2578063faf4d8cb146101bd578063fc97a303146101b85763fe9806b10361158f57611463565b611422565b6113fb565b6113b9565b611356565b61132d565b611306565b6112d7565b611295565b611274565b6110e7565b6110b6565b611062565b61102b565b610fce565b610faa565b610f8e565b610e30565b610dc3565b610d59565b610d38565b610d11565b610cf5565b610cd9565b610b17565b610a9e565b610a09565b610982565b6107eb565b6107ca565b6107a1565b610785565b610760565b61072c565b610703565b6106e7565b61050e565b3461028857600036600319011261028857602060405160018152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b038211176102be57604052565b61028d565b608081019081106001600160401b038211176102be57604052565b604081019081106001600160401b038211176102be57604052565b606081019081106001600160401b038211176102be57604052565b90601f801991011681019081106001600160401b038211176102be57604052565b60405190610342826102a3565b565b6004359063ffffffff8216820361028857565b6084359063ffffffff8216820361028857565b60c4359063ffffffff8216820361028857565b6024359063ffffffff8216820361028857565b6064359063ffffffff8216820361028857565b60a4359063ffffffff8216820361028857565b359063ffffffff8216820361028857565b60a090600319011261028857604051906103e0826102a3565b8163ffffffff600435818116810361028857825260243590811681036102885760208201526044356001600160401b038116810361028857604082015260643560608201526080608435910152565b91908260a091031261028857604051610447816102a3565b8092610452816103b6565b8252610460602082016103b6565b602083015260408101356001600160401b03811681036102885760809182916040850152606081013560608501520135910152565b6001600160401b0381116102be5760051b60200190565b6001600160401b0381116102be57601f01601f191660200190565b81601f82011215610288578035906104de826104ac565b926104ec6040519485610314565b8284526020838301011161028857816000926020809301838601378301015290565b346102885760c036600319011261028857610528366103c7565b60a435906001600160401b039081831161028857366023840112156102885782600401359261055684610495565b92604061056581519586610314565b8585526020958686019160249384600593841b8701019536871161028857858101945b87861061059b576105998a8a611637565b005b853583811161028857820190608060231983360301126102885784516105c0816102c3565b88830135815260449283810135868111610288576105e48f918c36918501016104c7565b908301526064938482013587811161028857820136604382011215610288578f9582828b948f8e95013561062361061a82610495565b97519788610314565b8087528a8701951b82010192368411610288579082918a999897969594938201945b84861061067357505050505050610663916084918a850152016103b6565b6060820152815201950194610588565b909192939480969798999a5035908d8211610288578e918401916043198336030112610288578d908f516106a6816102de565b86840135928311610288576106cf858f956106c787968b36918401016104c7565b8452016103b6565b838201528152019501939291908a9998979695610645565b3461028857600036600319011261028857602060405160f08152f35b34610288576000366003190112610288576034546040516001600160a01b039091168152602090f35b34610288576020366003190112610288576004356000526000602052602060018060a01b0360406000205416604051908152f35b3461028857600036600319011261028857602060355460ff60405191831c1615158152f35b3461028857600036600319011261028857602060405160008152f35b34610288576000366003190112610288576032546040516001600160a01b039091168152602090f35b3461028857600036600319011261028857602060345460c01c604051908152f35b34610288576020366003190112610288576004356001600160401b038082116102885736602383011215610288578160040135908111610288578101602401368111610288576001600160a01b0390307f00000000000000000000000080d12a78efe7604f00ed07ab2f16f643301674d583161461095957816108b96108826108d693602461092c9761087c613adf565b01611497565b951692166108918115156114cb565b61089c8315156114fc565b60018060a01b03166001600160601b0360a01b6032541617603255565b60018060a01b03166001600160601b0360a01b6033541617603355565b6108ed6108e1610335565b63ffffffff9092168252565b60006020820152600060408201527fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470806060830152608082015261152d565b60008052603c6020527f33d13c149959174817b07214f6faff3d4c1d39ff89c6e7f82e2df5f04c00a0ec55005b60405162461bcd60e51b81526020600482015260016024820152601960f91b6044820152606490fd5b3461028857600036600319011261028857602063ffffffff60355416604051908152f35b6004359060ff8216820361028857565b60a4359060ff8216820361028857565b6084359060ff8216820361028857565b6044359061ffff8216820361028857565b6064359061ffff8216820361028857565b6004359061ffff8216820361028857565b3461028857608036600319011261028857610a22610344565b6024359060ff821680920361028857610a9260ff91610a7f602094610a456109d6565b9063ffffffff610a536109e7565b941660005260388752604060002090600052865260406000209061ffff16600052602052604060002090565b9061ffff16600052602052604060002090565b54166040519015158152f35b346102885760203660031901126102885760ff610ab96109a6565b166000526041602052602060018060a01b0360406000205416604051908152f35b6001600160a01b0381160361028857565b604435906001600160801b038216820361028857565b602435906001600160801b038216820361028857565b346102885760e0366003190112610288577f05bb9b3f234e9dac83713761242d40320705b4204ba263981970118574d999cb600435610b5581610ada565b60243590610b6282610ada565b610cba610b6d610aeb565b610b756109e7565b610b7d610357565b610b856109b6565b90610b8e61036a565b92610b9f84848484898d8d33613182565b94610c4e610c3a60018060a01b03808c166000808d6040519460208601916323b872dd60e01b83523360248801521660448601526001600160801b038d16606486015260648552610bef856102a3565b60405194610bfc866102de565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af1610c34612e63565b91612f87565b8051908115918215610cbf575b5050612edc565b604080513381526001600160a01b03998a16602082015299909816978901979097526001600160801b03968716606089015261ffff16608088015263ffffffff90811660a088015260ff9190911660c08701521660e08501529091166101008301528190610120820190565b0390a1005b610cd29250602080918301019101612ec4565b3880610c47565b3461028857600036600319011261028857602060405160088152f35b3461028857600036600319011261028857602060405160078152f35b3461028857600036600319011261028857602063ffffffff60345460a01c16604051908152f35b3461028857600036600319011261028857602060325460c01c604051908152f35b34610288576000366003190112610288576040517f000000000000000000000000413552461b0b2c13f117d885b52aaa2f23374b1d6001600160a01b03168152602090f35b634e487b7160e01b600052602160045260246000fd5b600e1115610dbe57565b610d9e565b34610288576020366003190112610288576004356001600160401b039081811680910361028857600052603960205260406000205460ff8160e01c1691604051916001600160601b03198160601b16835260a01c166020820152600e821015610dbe576060916040820152f35b3461028857602080600319360112610288576001600160401b03600435818111610288573660238201121561028857806004013591610e6e83610495565b92604090610e7e82519586610314565b8085528585019160249384600593841b8701019536871161028857858101945b878610610eae5761059989612035565b85358381116102885782019060c0602319833603011261028857845191610ed4836102de565b610ee0368a830161042f565b835260c481013590858211610288570191366043840112156102885788830135610f0981610495565b90610f1688519283610314565b8082528d820190604480918b1b8701019036821161028857908f9691828189989796959401935b838510610f5857505050505083820152815201950194610e9e565b90919293809596979850358b8111610288578991610f7c83928636918701016104c7565b81520194019291908897969594610f3d565b3461028857600036600319011261028857602060405160068152f35b3461028857600036600319011261028857602060355460ff60405191831c16158152f35b34610288576040366003190112610288576020611022600435610ff081610ada565b610ff861037d565b9060018060a01b0316600052603a835260406000209063ffffffff16600052602052604060002090565b54604051908152f35b346102885760203660031901126102885763ffffffff611049610344565b16600052603c6020526020604060002054604051908152f35b3461028857604036600319011261028857602061102260043561108481610ada565b61108c61037d565b9060018060a01b0316600052603b835260406000209063ffffffff16600052602052604060002090565b34610288576020366003190112610288576004356000526037602052602060ff604060002054166040519015158152f35b60c0366003190112610288576004356110ff81610ada565b611107610b01565b61110f6109d6565b90611118610390565b926111216109c6565b6111296103a3565b6001600160a01b0383169190611144818389898988336130bd565b916001600160801b038316978834039734891161126f57899560008080809d81945af161116f612e63565b5015611245577f05bb9b3f234e9dac83713761242d40320705b4204ba263981970118574d999cb988a611220973403611226575b5050604080513381526001600160a01b03909816602089015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee908801526001600160801b03978816606088015261ffff16608087015263ffffffff90811660a087015260ff90911660c08601521660e084015292909216610100820152908190610120820190565b0390a180f35b80808061123e94335af1611238612e63565b50612e93565b388a6111a3565b60405162461bcd60e51b8152602060048201526002602482015261045360f41b6044820152606490fd5b6118ff565b3461028857600036600319011261028857602060335460c01c604051908152f35b34610288576020366003190112610288576004356112b281610ada565b60018060a01b03166000526042602052602060ff604060002054166040519015158152f35b3461028857600036600319011261028857602060405173374632e7d48b7872d904524fdc5dd4516f42cdff8152f35b3461028857600036600319011261028857602063ffffffff60325460a01c16604051908152f35b34610288576000366003190112610288576033546040516001600160a01b039091168152602090f35b346102885760203660031901126102885761ffff6113726109f8565b16600052603f602052608060406000205460ff6040519181811615158352818160081c161515602084015260018060a01b038160101c16604084015260b01c166060820152f35b34610288576020366003190112610288576004356113d681610ada565b60018060a01b0316600052603e602052602060ff604060002054166040519015158152f35b3461028857600036600319011261028857602063ffffffff60335460a01c16604051908152f35b346102885760203660031901126102885760043561143f81610ada565b60018060a01b03166000526040602052602061ffff60406000205416604051908152f35b346102885760203660031901126102885760ff61147e6109a6565b16600052603d6020526020604060002054604051908152f35b908160609103126102885780356114ad81610ada565b916114c8604060208401356114c181610ada565b93016103b6565b90565b156114d257565b60405162461bcd60e51b8152602060048201526002602482015261069360f41b6044820152606490fd5b1561150357565b60405162461bcd60e51b8152602060048201526002602482015261349960f11b6044820152606490fd5b604051602081019163ffffffff808251168452602082015116604083015260806001600160401b0391826040820151166060850152606081015182850152015160a083015260a0825260c0820190828210908211176102be5760405251902090565b7f000000000000000000000000413552461b0b2c13f117d885b52aaa2f23374b1d6001600160a01b038116156115de576000808092368280378136915af43d82803e156115da573d90f35b3d90fd5b60405162461bcd60e51b81526020600482015260016024820152603560f81b6044820152606490fd5b1561160e57565b60405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606490fd5b9061164a60ff60355460201c1615611607565b6000338152603e60205260409161166660ff848420541661186d565b6001937f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf49385855403611869578580916002875583946116a88151151561189d565b6033546116e39060a01c63ffffffff16956116dd6116d68863ffffffff16600052603c602052604060002090565b549161152d565b146118ce565b86935b6117aa575b505050506117a7929161175b6117659261173661170a60345460c01c90565b6001600160401b0361172d61172160335460c01c90565b6001600160401b031690565b91161115611974565b6033805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b5163ffffffff1690565b907f81a92942d0f9c33b897a438384c9c3d88be397776138efa3ba1a4fc8b626842463ffffffff83169180a263ffffffff1663ffffffff196035541617603555565b55565b90919294815163ffffffff87169081101561185f57611856916117d06117d79285611940565b51906119fd565b956118236117fe6117ea60345460c01c90565b898701516001600160401b03165b90611959565b603480546001600160c01b031660c09290921b6001600160c01b031916919091179055565b61183561182f8861152d565b96611915565b956118508763ffffffff16600052603c602052604060002090565b55611915565b929190826116e6565b50948392506116eb565b8380fd5b1561187457565b60405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606490fd5b156118a457565b60405162461bcd60e51b8152602060048201526002602482015261066360f41b6044820152606490fd5b156118d557565b60405162461bcd60e51b8152602060048201526002602482015261663160f01b6044820152606490fd5b634e487b7160e01b600052601160045260246000fd5b63ffffffff80911690811461126f5760010190565b634e487b7160e01b600052603260045260246000fd5b80518210156119545760209160051b010190565b61192a565b9190916001600160401b038080941691160191821161126f57565b1561197b57565b60405162461bcd60e51b8152602060048201526002602482015261331960f11b6044820152606490fd5b604051906119b2826102a3565b60006080838281528260208201528260408201528260608201520152565b90600163ffffffff8093160191821161126f57565b91909163ffffffff8080941691160191821161126f57565b90611a066119a5565b50606081019063ffffffff80835116908451161015611ab957611aac611a5c611a9c611a78611a736020611a67611a3c88611bdc565b9097919960808d015190611a548d5163ffffffff1690565b905191612347565b985163ffffffff1690565b98015163ffffffff1690565b6119d0565b611a8f611a83610335565b63ffffffff9098168852565b63ffffffff166020870152565b6001600160401b03166040850152565b6060830152608082015290565b60405162461bcd60e51b8152602060048201526002602482015261067360f41b6044820152606490fd5b15611aea57565b60405162461bcd60e51b8152602060048201526002602482015261068360f41b6044820152606490fd5b600019811461126f5760010190565b906001820180921161126f57565b906002820180921161126f57565b90600c820180921161126f57565b15611b5457565b60405162461bcd60e51b8152602060048201526002602482015261683160f01b6044820152606490fd5b15611b8557565b60405162461bcd60e51b8152602060048201526002602482015261341960f11b6044820152606490fd5b8051156119545760200190565b908151811015611954570160200190565b60ff16600e811015610dbe5790565b60209081810151611bf8611bf282516017900690565b15611ae3565b611c12611c0760325460c01c90565b60345460c01c6117f8565b6000937fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47092839660005b60408701518051821015611d3457611ce8969798611cdf611c6084611cf694611940565b51611c7d611c748983015163ffffffff1690565b63ffffffff1690565b90611c92611c8a83611b23565b895111611b4d565b611c9f6017830615611b7e565b611ccb611cc6611cc0611cb2858c611bbc565b516001600160f81b03191690565b60f81c90565b611bcd565b9188611cd7868d611959565b925193611e15565b99919092611959565b989060208151019181522090565b958051611d14575b50611d0a604091611b14565b9050969596611c3c565b611d2c611d0a916040939b9060208151019181522090565b999150611cfe565b5050939450505050565b604051602081018181106001600160401b038211176102be5760405260008152906000368137565b60405190611d73826102c3565b604582526060366020840137565b90611d8b826104ac565b611d986040519182610314565b8281528092611da9601f19916104ac565b0190602036910137565b15611dba57565b60405162461bcd60e51b81526020600482015260026024820152616b3160f01b6044820152606490fd5b15611deb57565b60405162461bcd60e51b815260206004820152600260248201526106b360f41b6044820152606490fd5b9293600090611e22611d3e565b94611e2c81610db4565b60018103611e7657505050611e64611e4a611e7193611e6b9361340c565b946001600160401b03166000526039602052604060002090565b5460601b90565b83613541565b600192565b611e87819895949398979297610db4565b60068103611f6857505090611e9b9161340c565b92611ea584613a0d565b815190919015611ec15761034291611ebc9161238e565b611de4565b5080611f2e6040611f1d611efe611ee4606061034297015160018060a01b031690565b6001600160a01b03166000908152603a6020526040902090565b608085015163ffffffff1663ffffffff16600052602052604060002090565b549201516001600160601b03191690565b604051611f5f81611f516020820194856014916001600160601b03191681520190565b03601f198101835282610314565b51902014611db3565b93955091935091611f7881610db4565b60038103611f9957505090611f8c9161340c565b905b6114c882518361339b565b611fa281610db4565b60078103611fbc57505090611fb69161340c565b90611f8e565b600591949550611fcb81610db4565b0361200b57611e64611fe361200393611ffd9361340c565b936001600160401b03166000526039602052604060002090565b8261378b565b600191611f8e565b60405162461bcd60e51b8152602060048201526002602482015261359960f11b6044820152606490fd5b6035549060209161204b60ff82851c1615611607565b6000338152603e8452604061206560ff828420541661186d565b6001947f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf49486865403611869579186806120d3926120d89460028a556120af875163ffffffff1690565b966120f5611c74896120ea6120de63ffffffff9b8c9484868096169d8e1515612285565b6122b6565b86611940565b51515163ffffffff1690565b169b168b11156122cc565b60325460a01c63ffffffff169689968a965b6121f8575b505050505050507f036cca5fc51c8ae4669768ca5966397d55516afb697541fdd4fe7e6b253c075c93926121f3926121c96121a48461218161215c6121ce9761215760325460c01c90565b611959565b603280546001600160c01b031660c09290921b6001600160c01b031916919091179055565b6121996117fe8261219460345460c01c90565b61232e565b60335460c01c61232e565b603380546001600160c01b031660c09290921b6001600160c01b031916919091179055565b6119e5565b6032805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b80a255565b909192939495968288168281101561227b5761226f916117f8886122699361225a6122328f8f611a739061222b926119e5565b928b611940565b51916122546122498d85510163ffffffff90511690565b8b16828c16146122fd565b8261292e565b5101516001600160401b031690565b97611915565b95949392919085612107565b509686955061210c565b1561228c57565b60405162461bcd60e51b8152602060048201526002602482015261064360f41b6044820152606490fd5b63ffffffff908116600019019190821161126f57565b156122d357565b60405162461bcd60e51b8152602060048201526002602482015261643160f01b6044820152606490fd5b1561230457565b60405162461bcd60e51b8152602060048201526002602482015261321960f11b6044820152606490fd5b6001600160401b03918216908216039190821161126f57565b929091604051926020840194855263ffffffff60e01b9060e01b166040840152604483015260648201526064815261237e816102a3565b51902090565b60021115610dbe57565b80511561195457602081015160f81c6002811015610dbe576123af81612384565b806123bf5750906114c89161252c565b806123cb600192612384565b036124eb576124a26123f4826124936124326123fa6123ec6124ae976134a3565b9590856134fb565b946134fb565b6040890151909491506001600160601b03191660408051602081019384526001600160601b0319909216908201529182906054820190565b0391612446601f1993848101835282610314565b5190206040516001600160f81b03196020820190815260609690961b6001600160601b03191660218201526035810191909152605581019390935282607581015b03908101835282610314565b5190206001600160a01b031690565b6001600160a01b031690565b60608201516124c5906001600160a01b03166124a2565b6001600160a01b039091161490816124db575090565b6080015163ffffffff1615919050565b5050600090565b60005b8381106125055750506000910152565b81810151838201526020016124f5565b90612528602092828151948592016124f2565b0190565b6124a2606061267f61254061268e94613455565b90506125f261248761267661257461256f6124a261256960408d01516001600160601b03191690565b60601c90565b612838565b61261c8a6125ec6126056125b16125a0611c7460206125a56125a0611c7460808a015163ffffffff1690565b61269d565b96015163ffffffff1690565b926125ec604051998a976125ec60208a016019907f4368616e67655075624b65790a5075624b6579486173683a200000000000000081520190565b90612515565b6652737b731b29d160c51b815260080190565b6a520b1b1b7bab73a24b21d160a51b8152600c0190565b0361262f601f1991828101865285610314565b612639845161269d565b936040519384916125ec60208401976125ec89601a907f19457468657265756d205369676e6564204d6573736167653a0a00000000000081520190565b51902090613b42565b9301516001600160a01b031690565b6001600160a01b039091161490565b806000917a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000808210156127d2575b506d04ee2d6d415b85acef8100000000808310156127c3575b50662386f26fc10000808310156127b4575b506305f5e100808310156127a5575b5061271080831015612796575b506064821015612786575b600a8092101561277c575b600190816021612734828701611d81565b95860101905b612746575b5050505090565b600019019083906f181899199a1a9b1b9c1cb0b131b232b360811b8282061a8353049182156127775791908261273a565b61273f565b9160010191612723565b9190606460029104910191612718565b6004919392049101913861270d565b60089193920491019138612700565b601091939204910191386126f1565b602091939204910191386126df565b6040935081049150386126c6565b801561126f576000190190565b156127f457565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b60405190612845826102f9565b602a82526040366020840137603061285c83611baf565b5381516001908110156119545790607860218401536029915b80831161288857506114c89150156127ed565b90600f8116906010821015611954576128c5916f181899199a1a9b1b9c1cb0b131b232b360811b901a6128bb8587611bbc565b5360041c926127e0565b9190612875565b156128d357565b60405162461bcd60e51b815260206004820152600260248201526106d360f41b6044820152606490fd5b1561290457565b60405162461bcd60e51b81526020600482015260026024820152616d3360f01b6044820152606490fd5b9061295e90612957612940845161152d565b9163ffffffff16600052603c602052604060002090565b54146128cc565b60007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4705b6020908184015180519063ffffffff851691821015612b8257906129a591611940565b51916129b9611cc6611cc0611cb286611baf565b6129c281610db4565b60038103612a74575091612a6891612a5b612a6e946129f06129e385613846565b9182015163ffffffff1690565b906129ff604082015160ff1690565b60c082015163ffffffff1660a08301516001600160a01b0316606084015161ffff1691612a3660808601516001600160801b031690565b93612a55610100612a4c60e089015161ffff1690565b97015160ff1690565b96612ce2565b9060208151019181522090565b91611915565b90612982565b612a7d81610db4565b60078103612b05575091612a6891612b00612a6e94612a9e6129e385613920565b90612aad604082015160ff1690565b606082015163ffffffff166101008301516001600160a01b031690612ad760a085015161ffff1690565b92612afa60e0612af160c08801516001600160801b031690565b96015160ff1690565b95612bc7565b612a5b565b60059150612b1281610db4565b03612b585781612a6891612b00612b2b612a6e956135a6565b6080810151606082015160c0909201516001600160801b0316916001600160a01b03169061ffff16612db1565b60405162461bcd60e51b8152602060048201526002602482015261369960f11b6044820152606490fd5b5050915050606061034292510151146128fd565b15612b9d57565b60405162461bcd60e51b815260206004820152600260248201526106f360f41b6044820152606490fd5b612c1560019260ff9296959661ffff8716600052603f6020528760406000205496612bf3868916612b96565b612c02868960b01c168c612d31565b93878060a01b03809960101c16936131ec565b961603612c6b5750505050612c44612c37826000526037602052604060002090565b805460ff19166001179055565b7f2697a58ffa01960ced18161304752c663ba354619840bdf68d763b1b88c09781600080a2565b612c8f612c82866000526000602052604060002090565b546001600160a01b031690565b908116612cd65750612cd182612cb2610342966000526000602052604060002090565b80546001600160a01b0319166001600160a01b03909216919091179055565b612db1565b91506103429350612db1565b60019293959660ff92612c159261ffff8816600052603f6020528860406000205497612d0f878a16612b96565b612d1e878a60b01c168d612d31565b93888060a01b03809a60101c169361330d565b9060ff1660120360ff811161126f5760ff16604d811161126f57600a0a600160801b811015612d87576001600160801b03809116918215612d7157160490565b634e487b7160e01b600052601260045260246000fd5b60405162461bcd60e51b8152602060048201526002602482015261189b60f11b6044820152606490fd5b919060018060a01b0316918260005260366020526001600160801b038080612deb8460406000209061ffff16600052602052604060002090565b541693168093019080821161126f577f3cfb74f0f066330f203d8ac39c3fef52fc056de4d011fc7d91dadd9ba69834169260209261ffff928760005260368552612e478360406000209061ffff16600052602052604060002090565b91166001600160801b03198254161790556040519485521692a3565b3d15612e8e573d90612e74826104ac565b91612e826040519384610314565b82523d6000602084013e565b606090565b15612e9a57565b60405162461bcd60e51b8152602060048201526002602482015261453160f01b6044820152606490fd5b90816020910312610288575180151581036102885790565b15612ee357565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b15612f4257565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b91929015612fa75750815115612f9b575090565b6114c8903b1515612f3b565b825190915015612fba5750805190602001fd5b6044604051809262461bcd60e51b825260206004830152612fea81518092816024860152602086860191016124f2565b601f01601f19168101030190fd5b15612fff57565b60405162461bcd60e51b8152602060048201526002602482015261241960f11b6044820152606490fd5b1561303057565b60405162461bcd60e51b8152602060048201526002602482015261483360f01b6044820152606490fd5b9061ffff8092166127100391821161126f57565b9190916001600160801b038080941691160291821691820361126f57565b1561309357565b60405162461bcd60e51b8152602060048201526002602482015261120d60f21b6044820152606490fd5b9396956001600160a01b03938483169390929091841561315857610342976130ef61312596612cb2988a161415612ff8565b6127106001600160801b0361311d61ffff61310d84828b1610613029565b6131168961305a565b168761306e565b16049a613267565b6131486131426124a2612c82846000526000602052604060002090565b1561308c565b6000526000602052604060002090565b60405162461bcd60e51b8152602060048201526002602482015261483160f01b6044820152606490fd5b9497966001600160a01b039485831694909390929091851561315857610342986131b661312597612cb2998b161415612ff8565b6127106001600160801b036131e461ffff6131d484828c1610613029565b6131dd8a61305a565b168861306e565b16049b61330d565b94929193909460405194602086019663ffffffff60e01b809360e01b16885260ff60f81b9060f81b16602487015260e01b1660258501526001600160601b0319809260601b16602985015260601b16603d8301526001600160801b03199060801b166051820152600060618201526043815261237e816102c3565b60405160e091821b6001600160e01b03199081166020830190815260f89490941b6001600160f81b03191660248301529390911b909216602583015260609290921b6001600160601b031916602982015273777777777777777777777777777777777777777760611b603d82015260809290921b6001600160801b031916605183015260f09290921b6001600160f01b03191660618201525b6043815261237e816102c3565b60405160e091821b6001600160e01b03199081166020830190815260f89490941b6001600160f81b03191660248301529390911b9092166025830152606092831b6001600160601b031990811660298401529390921b909216603d83015260809290921b6001600160801b031916605182015260f09290921b6001600160f01b031916606183015290613300565b90808251106133e3576133ad81611d81565b91816133b857505090565b602091830182019082018383015b8281106133d4575050505090565b815181529083019083016133c6565b60405162461bcd60e51b81526020600482015260016024820152602d60f91b6044820152606490fd5b908151604582019081831161126f57106133e357613428611d66565b9160209160658401910182018284015b828110613446575050505090565b81518152908301908301613438565b60428151106133e3576040519061346b826102c3565b6041825260209081830160603682376021606185019201905b8281106134945750505050604291565b81518152908301908301613484565b90601591828151106134b55782015190565b60405162461bcd60e51b81526020600482015260016024820152602960f91b6044820152606490fd5b60148201929183811161126f57838251106134b557016014015190565b60208201929183811161126f578382511061351857016020015190565b60405162461bcd60e51b81526020600482015260016024820152605960f81b6044820152606490fd5b60209060376001600160601b0319928392012060601b1691160361356157565b60405162461bcd60e51b815260206004820152601860248201527f4f503a20696e76616c6964206465706f736974206861736800000000000000006044820152606490fd5b906040519160e083018381106001600160401b038211176102be57604052600090818452602084018281526040850183815260608601848152608087019185835260a088019386855260c089019687528861360087613682565b60ff1690915261361090876136ff565b63ffffffff16909152613623908661369a565b60ff1690915261363290611b3f565b61363c90856134de565b6001600160a01b031690915261365290846136b9565b61ffff1690915261366390836136b9565b61ffff1690915261367391613745565b6001600160801b031690915250565b90600291805160011015611954576021015160f81c90565b9190600181019081811161126f576136b29193611bbc565b5160f81c90565b60028201929183811161126f57838251106136d657016002015190565b60405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606490fd5b60048201929183811161126f578382511061371c57016004015190565b60405162461bcd60e51b81526020600482015260016024820152602b60f91b6044820152606490fd5b60108201929183811161126f578382511061376257016010015190565b60405162461bcd60e51b81526020600482015260016024820152605760f81b6044820152606490fd5b602090602b6001600160601b0319928392012060601b169116036137ab57565b60405162461bcd60e51b815260206004820152601960248201527f4f503a20696e76616c69642066756c6c457869742068617368000000000000006044820152606490fd5b6040519061012082018281106001600160401b038211176102be57604052816101006000918281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201520152565b9061384f6137f0565b9161385981613682565b60ff16845261386890826136ff565b63ffffffff16602085015261387d908261369a565b60ff16604085015261388f90826136b9565b61ffff1660608501526138a190611b31565b6138ab9082613745565b6001600160801b031660808501526138c290611b31565b6138cb90611b3f565b6138d590826134de565b6001600160a01b031660a08501526138ed90826136ff565b63ffffffff1660c085015261390290826136b9565b61ffff1660e08501526139149161369a565b60ff1661010084015250565b906103426139fb836139f56139f06139e56139df6139ce6139c861ffff6139ba6139b461394b6137f0565b9d8e60806139a861396c60ff61396086613682565b919091168552856136ff565b909461399a60ff61398b63ffffffff988980961660208a01528461369a565b919091166040880152826136ff565b9290921660608601526136ff565b93909316910152611b23565b896136b9565b9190911660a08d0152611b31565b86613745565b6001600160801b031660c08b015290565b8461369a565b60ff1660e089015290565b611b3f565b906134de565b6001600160a01b031661010085015250565b90613a166119a5565b9163ffffffff613a3660ff613a2a84613682565b919091168652836136ff565b919091166020850152600181019081811161126f576015810180921161126f5781835110613ab65782613a92613a8c613aa8946139f06021613aa3966103429901015160408b01906001600160601b0319169052565b826134de565b6001600160a01b0316606088015291565b6136ff565b63ffffffff16608085015250565b60405162461bcd60e51b81526020600482015260016024820152605360f81b6044820152606490fd5b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4600181549155613b0c57565b60405162461bcd60e51b815260206004820152600260248201526118a160f11b6044820152606490fd5b6040513d6000823e3d90fd5b6041815103613b91576020818101516040808401516060948501518251968752600090811a8786015291860192909252928401528180529160809060015afa15613b8c5760005190565b613b36565b60405162461bcd60e51b815260206004820152600360248201526207574360ec1b6044820152606490fdfea2646970667358221220d8b3407da7d8cc03d7000b15cbddf506338e72da9ef707ab362a0c896392a15c64736f6c63430008120033

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

000000000000000000000000413552461b0b2c13f117d885b52aaa2f23374b1d

-----Decoded View---------------
Arg [0] : _periphery (address): 0x413552461b0b2c13f117d885b52AaA2f23374B1D

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000413552461b0b2c13f117d885b52aaa2f23374b1d


Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

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

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.