View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ZkLink
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
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;
}
}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;
}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)));
}
}{
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
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"}]Contract Creation Code
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
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$15,995.26
Net Worth in ETH
Token Allocations
USDC
98.80%
USDT
0.55%
BRETT
0.34%
Others
0.31%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| BASE | 98.80% | $0.999702 | 15,808.5235 | $15,803.81 | |
| BASE | 0.55% | $0.998299 | 87.3301 | $87.18 | |
| BASE | 0.34% | $0.011834 | 4,630.1397 | $54.79 | |
| BASE | 0.14% | $0.000909 | 25,481.1206 | $23.16 | |
| BASE | 0.07% | $0.999617 | 11.5055 | $11.5 | |
| BASE | 0.04% | <$0.000001 | 651,551,625 | $6.25 | |
| BASE | 0.02% | $0.019895 | 160 | $3.18 | |
| BASE | <0.01% | $0.000009 | 174,669.1504 | $1.51 | |
| BASE | <0.01% | $0.000001 | 740,000 | $0.7769 | |
| BASE | <0.01% | $0.001653 | 158.329 | $0.2617 | |
| ARB | 0.02% | $2,695.96 | 0.00104633 | $2.82 |
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.