Source Code
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| - | 12910538 | 1695 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
ZkSync
Compiler Version
v0.7.6+commit.7338295f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./ReentrancyGuard.sol";
import "./SafeMath.sol";
import "./SafeMathUInt128.sol";
import "./SafeCast.sol";
import "./Utils.sol";
import "./Storage.sol";
import "./Config.sol";
import "./Events.sol";
import "./Bytes.sol";
import "./Operations.sol";
import "./UpgradeableMaster.sol";
import "./RegenesisMultisig.sol";
import "./AdditionalZkSync.sol";
/// @title zkSync main contract
/// @author Matter Labs
contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
using SafeMath for uint256;
using SafeMathUInt128 for uint128;
bytes32 private constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
/// @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
struct CommitBlockInfo {
bytes32 newStateHash;
bytes publicData;
uint256 timestamp;
OnchainOperationData[] onchainOperations;
uint32 blockNumber;
uint32 feeAccount;
}
/// @notice Data needed to execute committed and verified block
/// @param commitmentsInSlot verified commitments in one slot
/// @param commitmentIdx index such that commitmentsInSlot[commitmentIdx] is current block commitment
struct ExecuteBlockInfo {
StoredBlockInfo storedBlock;
bytes[] pendingOnchainOpsPubdata;
}
/// @notice Recursive proof input data (individual commitments are constructed onchain)
struct ProofInput {
uint256[] recursiveInput;
uint256[] proof;
uint256[] commitments;
uint8[] vkIndexes;
uint256[16] subproofsLimbs;
}
// Upgrade functional
/// @notice Notice period before activation preparation status of upgrade mode
function getNoticePeriod() external pure override returns (uint256) {
return 0;
}
/// @notice Notification that upgrade notice period started
/// @dev Can be external because Proxy contract intercepts illegal calls of this function
function upgradeNoticePeriodStarted() external override {
upgradeStartTimestamp = block.timestamp;
}
/// @notice Notification that upgrade preparation status is activated
/// @dev Can be external because Proxy contract intercepts illegal calls of this function
function upgradePreparationStarted() external override {
upgradePreparationActive = true;
upgradePreparationActivationTime = block.timestamp;
require(block.timestamp >= upgradeStartTimestamp.add(approvedUpgradeNoticePeriod));
}
/// @dev When upgrade is finished or canceled we must clean upgrade-related state.
function clearUpgradeStatus() internal {
upgradePreparationActive = false;
upgradePreparationActivationTime = 0;
approvedUpgradeNoticePeriod = UPGRADE_NOTICE_PERIOD;
emit NoticePeriodChange(approvedUpgradeNoticePeriod);
upgradeStartTimestamp = 0;
for (uint256 i = 0; i < SECURITY_COUNCIL_MEMBERS_NUMBER; ++i) {
securityCouncilApproves[i] = false;
}
numberOfApprovalsFromSecurityCouncil = 0;
}
/// @notice Notification that upgrade canceled
/// @dev Can be external because Proxy contract intercepts illegal calls of this function
function upgradeCanceled() external override {
clearUpgradeStatus();
}
/// @notice Notification that upgrade finishes
/// @dev Can be external because Proxy contract intercepts illegal calls of this function
function upgradeFinishes() external override {
clearUpgradeStatus();
}
/// @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 zkSync contract initialization. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param initializationParameters Encoded representation of initialization parameters:
/// @dev _governanceAddress The address of Governance contract
/// @dev _verifierAddress The address of Verifier contract
/// @dev _genesisStateHash Genesis blocks (first block) state tree root hash
function initialize(bytes calldata initializationParameters) external {
initializeReentrancyGuard();
(address _governanceAddress, address _verifierAddress, address _additionalZkSync, bytes32 _genesisStateHash) =
abi.decode(initializationParameters, (address, address, address, bytes32));
verifier = Verifier(_verifierAddress);
governance = Governance(_governanceAddress);
additionalZkSync = AdditionalZkSync(_additionalZkSync);
StoredBlockInfo memory storedBlockZero =
StoredBlockInfo(0, 0, EMPTY_STRING_KECCAK, 0, _genesisStateHash, bytes32(0));
storedBlockHashes[0] = hashStoredBlockInfo(storedBlockZero);
approvedUpgradeNoticePeriod = UPGRADE_NOTICE_PERIOD;
emit NoticePeriodChange(approvedUpgradeNoticePeriod);
}
/// @notice zkSync contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param upgradeParameters Encoded representation of upgrade parameters
// solhint-disable-next-line no-empty-blocks
function upgrade(bytes calldata upgradeParameters) external nonReentrant {}
function cutUpgradeNoticePeriod() external {
/// All functions delegated to additional contract should NOT be nonReentrant
delegateAdditional();
}
/// @notice Sends tokens
/// @dev NOTE: will revert if transfer call fails or rollup balance difference (before and after transfer) is bigger than _maxAmount
/// @dev This function is used to allow tokens to spend zkSync contract balance up to amount that is requested
/// @param _token Token address
/// @param _to Address of recipient
/// @param _amount Amount of tokens to transfer
/// @param _maxAmount Maximum possible amount of tokens to transfer to this account
function _transferERC20(
IERC20 _token,
address _to,
uint128 _amount,
uint128 _maxAmount
) external returns (uint128 withdrawnAmount) {
require(msg.sender == address(this), "5"); // wtg10 - can be called only from this contract as one "external" call (to revert all this function state changes if it is needed)
uint256 balanceBefore = _token.balanceOf(address(this));
require(Utils.sendERC20(_token, _to, _amount), "6"); // wtg11 - ERC20 transfer fails
uint256 balanceAfter = _token.balanceOf(address(this));
uint256 balanceDiff = balanceBefore.sub(balanceAfter);
require(balanceDiff <= _maxAmount, "7"); // wtg12 - rollup balance difference (before and after transfer) is bigger than _maxAmount
return SafeCast.toUint128(balanceDiff);
}
/// @notice Accrues users balances from deposit priority requests in Exodus mode
/// @dev WARNING: Only for Exodus mode
/// @dev Canceling may take several separate transactions to be completed
/// @param _n number of requests to process
function cancelOutstandingDepositsForExodusMode(uint64 _n, bytes[] memory _depositsPubdata) external {
/// All functions delegated to additional contract should NOT be nonReentrant
delegateAdditional();
}
/// @notice Deposit ETH to Layer 2 - transfer ether from user into contract, validate it, register deposit
/// @param _zkSyncAddress The receiver Layer 2 address
function depositETH(address _zkSyncAddress) external payable {
require(_zkSyncAddress != SPECIAL_ACCOUNT_ADDRESS, "P");
requireActive();
registerDeposit(0, SafeCast.toUint128(msg.value), _zkSyncAddress);
}
/// @notice Deposit ERC20 token to Layer 2 - transfer ERC20 tokens from user into contract, validate it, register deposit
/// @param _token Token address
/// @param _amount Token amount
/// @param _zkSyncAddress Receiver Layer 2 address
function depositERC20(
IERC20 _token,
uint104 _amount,
address _zkSyncAddress
) external nonReentrant {
require(_zkSyncAddress != SPECIAL_ACCOUNT_ADDRESS, "P");
requireActive();
// Get token id by its address
uint16 tokenId = governance.validateTokenAddress(address(_token));
require(!governance.pausedTokens(tokenId), "b"); // token deposits are paused
uint256 balanceBefore = _token.balanceOf(address(this));
require(Utils.transferFromERC20(_token, msg.sender, address(this), SafeCast.toUint128(_amount)), "c"); // token transfer failed deposit
uint256 balanceAfter = _token.balanceOf(address(this));
uint128 depositAmount = SafeCast.toUint128(balanceAfter.sub(balanceBefore));
require(depositAmount <= MAX_DEPOSIT_AMOUNT, "C");
registerDeposit(tokenId, depositAmount, _zkSyncAddress);
}
/// @notice Returns amount of tokens that can be withdrawn by `address` from zkSync contract
/// @param _address Address of the tokens owner
/// @param _token Address of token, zero address is used for ETH
function getPendingBalance(address _address, address _token) public view returns (uint128) {
uint16 tokenId = 0;
if (_token != address(0)) {
tokenId = governance.validateTokenAddress(_token);
}
return pendingBalances[packAddressAndTokenId(_address, tokenId)].balanceToWithdraw;
}
/// @notice Withdraws tokens from zkSync contract to the owner
/// @param _owner Address of the tokens owner
/// @param _token Address of tokens, zero address is used for ETH
/// @param _amount Amount to withdraw to request.
/// NOTE: We will call ERC20.transfer(.., _amount), but if according to internal logic of ERC20 token zkSync contract
/// balance will be decreased by value more then _amount we will try to subtract this value from user pending balance
function withdrawPendingBalance(
address payable _owner,
address _token,
uint128 _amount
) external nonReentrant {
if (_token == address(0)) {
registerWithdrawal(0, _amount, _owner);
(bool success, ) = _owner.call{value: _amount}("");
require(success, "d"); // ETH withdraw failed
} else {
uint16 tokenId = governance.validateTokenAddress(_token);
bytes22 packedBalanceKey = packAddressAndTokenId(_owner, tokenId);
uint128 balance = pendingBalances[packedBalanceKey].balanceToWithdraw;
// We will allow withdrawals of `value` such that:
// `value` <= user pending balance
// `value` can be bigger then `_amount` requested if token takes fee from sender in addition to `_amount` requested
uint128 withdrawnAmount = this._transferERC20(IERC20(_token), _owner, _amount, balance);
registerWithdrawal(tokenId, withdrawnAmount, _owner);
}
}
/// @notice Withdraws NFT from zkSync contract to the owner
/// @param _tokenId Id of NFT token
function withdrawPendingNFTBalance(uint32 _tokenId) external nonReentrant {
Operations.WithdrawNFT memory op = pendingWithdrawnNFTs[_tokenId];
require(op.creatorAddress != address(0), "op"); // No NFT to withdraw
NFTFactory _factory = governance.getNFTFactory(op.creatorAccountId, op.creatorAddress);
_factory.mintNFTFromZkSync(
op.creatorAddress,
op.receiver,
op.creatorAccountId,
op.serialId,
op.contentHash,
op.tokenId
);
// Save withdrawn nfts for future deposits
withdrawnNFTs[op.tokenId] = address(_factory);
emit WithdrawalNFT(op.tokenId);
delete pendingWithdrawnNFTs[_tokenId];
}
/// @notice Register full exit request - pack pubdata, add priority request
/// @param _accountId Numerical id of the account
/// @param _token Token address, 0 address for ether
function requestFullExit(uint32 _accountId, address _token) public nonReentrant {
requireActive();
require(_accountId <= MAX_ACCOUNT_ID, "e");
require(_accountId != SPECIAL_ACCOUNT_ID, "v"); // request full exit for nft storage account
uint16 tokenId;
if (_token == address(0)) {
tokenId = 0;
} else {
tokenId = governance.validateTokenAddress(_token);
}
// Priority Queue request
Operations.FullExit memory op =
Operations.FullExit({
accountId: _accountId,
owner: msg.sender,
tokenId: tokenId,
amount: 0, // unknown at this point
nftCreatorAccountId: uint32(0), // unknown at this point
nftCreatorAddress: address(0), // unknown at this point
nftSerialId: uint32(0), // unknown at this point
nftContentHash: bytes32(0) // unknown at this point
});
bytes memory pubData = Operations.writeFullExitPubdataForPriorityQueue(op);
addPriorityRequest(Operations.OpType.FullExit, pubData);
// User must fill storage slot of balancesToWithdraw(msg.sender, tokenId) with nonzero value
// In this case operator should just overwrite this slot during confirming withdrawal
bytes22 packedBalanceKey = packAddressAndTokenId(msg.sender, tokenId);
pendingBalances[packedBalanceKey].gasReserveValue = FILLED_GAS_RESERVE_VALUE;
}
/// @notice Register full exit nft request - pack pubdata, add priority request
/// @param _accountId Numerical id of the account
/// @param _tokenId NFT token id in zkSync network
function requestFullExitNFT(uint32 _accountId, uint32 _tokenId) public nonReentrant {
requireActive();
require(_accountId <= MAX_ACCOUNT_ID, "e");
require(_accountId != SPECIAL_ACCOUNT_ID, "v"); // request full exit nft for nft storage account
require(MAX_FUNGIBLE_TOKEN_ID < _tokenId && _tokenId < SPECIAL_NFT_TOKEN_ID, "T"); // request full exit nft for invalid token id
// Priority Queue request
Operations.FullExit memory op =
Operations.FullExit({
accountId: _accountId,
owner: msg.sender,
tokenId: _tokenId,
amount: 0, // unknown at this point
nftCreatorAccountId: uint32(0), // unknown at this point
nftCreatorAddress: address(0), // unknown at this point
nftSerialId: uint32(0), // unknown at this point
nftContentHash: bytes32(0) // unknown at this point
});
bytes memory pubData = Operations.writeFullExitPubdataForPriorityQueue(op);
addPriorityRequest(Operations.OpType.FullExit, pubData);
}
/// @dev Process one block commit using previous block StoredBlockInfo,
/// @dev returns new block StoredBlockInfo
/// @dev NOTE: Does not change storage (except events, so we can't mark it view)
function commitOneBlock(StoredBlockInfo memory _previousBlock, CommitBlockInfo memory _newBlock)
internal
view
returns (StoredBlockInfo memory storedNewBlock)
{
require(_newBlock.blockNumber == _previousBlock.blockNumber + 1, "f"); // only commit next block
// Check timestamp of the new block
{
require(_newBlock.timestamp >= _previousBlock.timestamp, "g"); // Block should be after previous block
bool timestampNotTooSmall = block.timestamp.sub(COMMIT_TIMESTAMP_NOT_OLDER) <= _newBlock.timestamp;
bool timestampNotTooBig = _newBlock.timestamp <= block.timestamp.add(COMMIT_TIMESTAMP_APPROXIMATION_DELTA);
require(timestampNotTooSmall && timestampNotTooBig, "h"); // New block timestamp is not valid
}
// Check onchain operations
(bytes32 pendingOnchainOpsHash, uint64 priorityReqCommitted, bytes memory onchainOpsOffsetCommitment) =
collectOnchainOps(_newBlock);
// Create block commitment for verification proof
bytes32 commitment = createBlockCommitment(_previousBlock, _newBlock, onchainOpsOffsetCommitment);
return
StoredBlockInfo(
_newBlock.blockNumber,
priorityReqCommitted,
pendingOnchainOpsHash,
_newBlock.timestamp,
_newBlock.newStateHash,
commitment
);
}
/// @notice Commit block
/// @notice 1. Checks onchain operations, timestamp.
/// @notice 2. Store block commitments
function commitBlocks(StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] memory _newBlocksData)
external
nonReentrant
{
requireActive();
governance.requireActiveValidator(msg.sender);
// Check that we commit blocks after last committed block
require(storedBlockHashes[totalBlocksCommitted] == hashStoredBlockInfo(_lastCommittedBlockData), "i"); // incorrect previous block data
for (uint32 i = 0; i < _newBlocksData.length; ++i) {
_lastCommittedBlockData = commitOneBlock(_lastCommittedBlockData, _newBlocksData[i]);
totalCommittedPriorityRequests += _lastCommittedBlockData.priorityOperations;
storedBlockHashes[_lastCommittedBlockData.blockNumber] = hashStoredBlockInfo(_lastCommittedBlockData);
emit BlockCommit(_lastCommittedBlockData.blockNumber);
}
totalBlocksCommitted += uint32(_newBlocksData.length);
require(totalCommittedPriorityRequests <= totalOpenPriorityRequests, "j");
}
/// @dev 1. Try to send token to _recipients
/// @dev 2. On failure: Increment _recipients balance to withdraw.
function withdrawOrStoreNFT(Operations.WithdrawNFT memory op) internal {
NFTFactory _factory = governance.getNFTFactory(op.creatorAccountId, op.creatorAddress);
try
_factory.mintNFTFromZkSync{gas: WITHDRAWAL_NFT_GAS_LIMIT}(
op.creatorAddress,
op.receiver,
op.creatorAccountId,
op.serialId,
op.contentHash,
op.tokenId
)
{
// Save withdrawn nfts for future deposits
withdrawnNFTs[op.tokenId] = address(_factory);
emit WithdrawalNFT(op.tokenId);
} catch {
pendingWithdrawnNFTs[op.tokenId] = op;
emit WithdrawalNFTPending(op.tokenId);
}
}
/// @dev 1. Try to send token to _recipients
/// @dev 2. On failure: Increment _recipients balance to withdraw.
function withdrawOrStore(
uint16 _tokenId,
address _recipient,
uint128 _amount
) internal {
bytes22 packedBalanceKey = packAddressAndTokenId(_recipient, _tokenId);
bool sent = false;
if (_tokenId == 0) {
address payable toPayable = address(uint160(_recipient));
sent = sendETHNoRevert(toPayable, _amount);
} else {
address tokenAddr = governance.tokenAddresses(_tokenId);
// We use `_transferERC20` here to check that `ERC20` token indeed transferred `_amount`
// and fail if token subtracted from zkSync balance more then `_amount` that was requested.
// This can happen if token subtracts fee from sender while transferring `_amount` that was requested to transfer.
try this._transferERC20{gas: WITHDRAWAL_GAS_LIMIT}(IERC20(tokenAddr), _recipient, _amount, _amount) {
sent = true;
} catch {
sent = false;
}
}
if (sent) {
emit Withdrawal(_tokenId, _amount);
} else {
increaseBalanceToWithdraw(packedBalanceKey, _amount);
emit WithdrawalPending(_tokenId, _amount);
}
}
/// @dev Executes one block
/// @dev 1. Processes all pending operations (Send Exits, Complete priority requests)
/// @dev 2. Finalizes block on Ethereum
/// @dev _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[_blockExecuteData.storedBlock.blockNumber],
"exe10" // executing block should be committed
);
require(_blockExecuteData.storedBlock.blockNumber == totalBlocksExecuted + _executedBlockIdx + 1, "k"); // Execute blocks in order
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]));
if (opType == Operations.OpType.PartialExit) {
Operations.PartialExit memory op = Operations.readPartialExitPubdata(pubData);
// Circuit guarantees that partial exits are available only for fungible tokens
require(op.tokenId <= MAX_FUNGIBLE_TOKEN_ID, "mf1");
withdrawOrStore(uint16(op.tokenId), op.owner, op.amount);
} else if (opType == Operations.OpType.ForcedExit) {
Operations.ForcedExit memory op = Operations.readForcedExitPubdata(pubData);
// Circuit guarantees that forced exits are available only for fungible tokens
require(op.tokenId <= MAX_FUNGIBLE_TOKEN_ID, "mf2");
withdrawOrStore(uint16(op.tokenId), op.target, op.amount);
} else if (opType == Operations.OpType.FullExit) {
Operations.FullExit memory op = Operations.readFullExitPubdata(pubData);
if (op.tokenId <= MAX_FUNGIBLE_TOKEN_ID) {
withdrawOrStore(uint16(op.tokenId), op.owner, op.amount);
} else {
if (op.amount == 1) {
Operations.WithdrawNFT memory withdrawNftOp =
Operations.WithdrawNFT(
op.nftCreatorAccountId,
op.nftCreatorAddress,
op.nftSerialId,
op.nftContentHash,
op.owner,
op.tokenId
);
withdrawOrStoreNFT(withdrawNftOp);
}
}
} else if (opType == Operations.OpType.WithdrawNFT) {
Operations.WithdrawNFT memory op = Operations.readWithdrawNFTPubdata(pubData);
withdrawOrStoreNFT(op);
} else {
revert("l"); // unsupported op in block execution
}
pendingOnchainOpsHash = Utils.concatHash(pendingOnchainOpsHash, pubData);
}
require(pendingOnchainOpsHash == _blockExecuteData.storedBlock.pendingOnchainOperationsHash, "m"); // incorrect onchain ops executed
}
/// @notice Execute blocks, completing priority operations and processing withdrawals.
/// @notice 1. Processes all pending operations (Send Exits, Complete priority requests)
/// @notice 2. Finalizes block on Ethereum
function executeBlocks(ExecuteBlockInfo[] memory _blocksData) external nonReentrant {
requireActive();
governance.requireActiveValidator(msg.sender);
uint64 priorityRequestsExecuted = 0;
uint32 nBlocks = uint32(_blocksData.length);
for (uint32 i = 0; i < nBlocks; ++i) {
executeOneBlock(_blocksData[i], i);
priorityRequestsExecuted += _blocksData[i].storedBlock.priorityOperations;
emit BlockVerification(_blocksData[i].storedBlock.blockNumber);
}
firstPriorityRequestId += priorityRequestsExecuted;
totalCommittedPriorityRequests -= priorityRequestsExecuted;
totalOpenPriorityRequests -= priorityRequestsExecuted;
totalBlocksExecuted += nBlocks;
require(totalBlocksExecuted <= totalBlocksProven, "n"); // Can't execute blocks more then committed and proven currently.
}
/// @notice Blocks commitment verification.
/// @notice Only verifies block commitments without any other processing
function proveBlocks(StoredBlockInfo[] memory _committedBlocks, ProofInput memory _proof) external nonReentrant {
requireActive();
uint32 currentTotalBlocksProven = totalBlocksProven;
for (uint256 i = 0; i < _committedBlocks.length; ++i) {
require(hashStoredBlockInfo(_committedBlocks[i]) == storedBlockHashes[currentTotalBlocksProven + 1], "o1");
++currentTotalBlocksProven;
require(_proof.commitments[i] & INPUT_MASK == uint256(_committedBlocks[i].commitment) & INPUT_MASK, "o"); // incorrect block commitment in proof
}
bool success =
verifier.verifyAggregatedBlockProof(
_proof.recursiveInput,
_proof.proof,
_proof.vkIndexes,
_proof.commitments,
_proof.subproofsLimbs
);
require(success, "p"); // Aggregated proof verification fail
require(currentTotalBlocksProven <= totalBlocksCommitted, "q");
totalBlocksProven = currentTotalBlocksProven;
}
/// @notice Reverts unverified blocks
function revertBlocks(StoredBlockInfo[] memory _blocksToRevert) external {
/// All functions delegated to additional contract should NOT be nonReentrant
delegateAdditional();
}
/// @notice Checks if Exodus mode must be entered. If true - enters exodus mode and emits ExodusMode event.
/// @dev Exodus mode must be entered in case of current ethereum block number is higher than the oldest
/// @dev of existed priority requests expiration block number.
/// @return bool flag that is true if the Exodus mode must be entered.
function activateExodusMode() public returns (bool) {
bool trigger =
block.number >= priorityRequests[firstPriorityRequestId].expirationBlock &&
priorityRequests[firstPriorityRequestId].expirationBlock != 0;
if (trigger) {
if (!exodusMode) {
exodusMode = true;
emit ExodusMode();
}
return true;
} else {
return false;
}
}
/// @notice Withdraws token from ZkSync to root chain in case of exodus mode. User must provide proof that he owns funds
/// @param _storedBlockInfo Last verified block
/// @param _owner Owner of the account
/// @param _accountId Id of the account in the tree
/// @param _proof Proof
/// @param _tokenId Verified token id
/// @param _amount Amount for owner (must be total amount, not part of it)
function performExodus(
StoredBlockInfo memory _storedBlockInfo,
address _owner,
uint32 _accountId,
uint32 _tokenId,
uint128 _amount,
uint32 _nftCreatorAccountId,
address _nftCreatorAddress,
uint32 _nftSerialId,
bytes32 _nftContentHash,
uint256[] memory _proof
) external {
/// All functions delegated to additional should NOT be nonReentrant
delegateAdditional();
}
/// @notice Set data for changing pubkey hash using onchain authorization.
/// Transaction author (msg.sender) should be L2 account address
/// @notice New pubkey hash can be reset, to do that user should send two transactions:
/// 1) First `setAuthPubkeyHash` transaction for already used `_nonce` will set timer.
/// 2) After `AUTH_FACT_RESET_TIMELOCK` time is passed second `setAuthPubkeyHash` transaction will reset pubkey hash for `_nonce`.
/// @param _pubkeyHash New pubkey hash
/// @param _nonce Nonce of the change pubkey L2 transaction
function setAuthPubkeyHash(bytes calldata _pubkeyHash, uint32 _nonce) external {
/// All functions delegated to additional contract should NOT be nonReentrant
delegateAdditional();
}
/// @notice Register deposit request - pack pubdata, add priority request and emit OnchainDeposit event
/// @param _tokenId Token by id
/// @param _amount Token amount
/// @param _owner Receiver
function registerDeposit(
uint16 _tokenId,
uint128 _amount,
address _owner
) internal {
// Priority Queue request
Operations.Deposit memory op =
Operations.Deposit({
accountId: 0, // unknown at this point
owner: _owner,
tokenId: _tokenId,
amount: _amount
});
bytes memory pubData = Operations.writeDepositPubdataForPriorityQueue(op);
addPriorityRequest(Operations.OpType.Deposit, pubData);
emit Deposit(_tokenId, _amount);
}
/// @notice Register withdrawal - update user balance and emit OnchainWithdrawal event
/// @param _token - token by id
/// @param _amount - token amount
/// @param _to - address to withdraw to
function registerWithdrawal(
uint16 _token,
uint128 _amount,
address payable _to
) internal {
bytes22 packedBalanceKey = packAddressAndTokenId(_to, _token);
uint128 balance = pendingBalances[packedBalanceKey].balanceToWithdraw;
pendingBalances[packedBalanceKey].balanceToWithdraw = balance.sub(_amount);
emit Withdrawal(_token, _amount);
}
/// @dev Gets operations packed in bytes array. Unpacks it and stores onchain operations.
/// @dev Priority operations must be committed in the same order as they are in the priority queue.
/// @dev NOTE: does not change storage! (only emits events)
/// @dev processableOperationsHash - hash of the all operations that needs to be executed (Deposit, Exits, ChangPubKey)
/// @dev priorityOperationsProcessed - number of priority operations processed in this block (Deposits, FullExits)
/// @dev offsetsCommitment - array where 1 is stored in chunk where onchainOperation begins and other are 0 (used in commitments)
function collectOnchainOps(CommitBlockInfo memory _newBlockData)
internal
view
returns (
bytes32 processableOperationsHash,
uint64 priorityOperationsProcessed,
bytes memory offsetsCommitment
)
{
bytes memory pubData = _newBlockData.publicData;
uint64 uncommittedPriorityRequestsOffset = firstPriorityRequestId + totalCommittedPriorityRequests;
priorityOperationsProcessed = 0;
processableOperationsHash = EMPTY_STRING_KECCAK;
require(pubData.length % CHUNK_BYTES == 0, "A"); // pubdata length must be a multiple of CHUNK_BYTES
offsetsCommitment = new bytes(pubData.length / CHUNK_BYTES);
for (uint256 i = 0; i < _newBlockData.onchainOperations.length; ++i) {
OnchainOperationData memory onchainOpData = _newBlockData.onchainOperations[i];
uint256 pubdataOffset = onchainOpData.publicDataOffset;
require(pubdataOffset < pubData.length, "A1");
require(pubdataOffset % CHUNK_BYTES == 0, "B"); // offsets should be on chunks boundaries
uint256 chunkId = pubdataOffset / CHUNK_BYTES;
require(offsetsCommitment[chunkId] == 0x00, "C"); // offset commitment should be empty
offsetsCommitment[chunkId] = bytes1(0x01);
Operations.OpType opType = Operations.OpType(uint8(pubData[pubdataOffset]));
if (opType == Operations.OpType.Deposit) {
bytes memory opPubData = Bytes.slice(pubData, pubdataOffset, DEPOSIT_BYTES);
Operations.Deposit memory depositData = Operations.readDepositPubdata(opPubData);
checkPriorityOperation(depositData, uncommittedPriorityRequestsOffset + priorityOperationsProcessed);
priorityOperationsProcessed++;
} else if (opType == Operations.OpType.ChangePubKey) {
bytes memory opPubData = Bytes.slice(pubData, pubdataOffset, CHANGE_PUBKEY_BYTES);
Operations.ChangePubKey memory op = Operations.readChangePubKeyPubdata(opPubData);
if (onchainOpData.ethWitness.length != 0) {
bool valid = verifyChangePubkey(onchainOpData.ethWitness, op);
require(valid, "D"); // failed to verify change pubkey hash signature
} else {
bool valid = authFacts[op.owner][op.nonce] == keccak256(abi.encodePacked(op.pubKeyHash));
require(valid, "E"); // new pub key hash is not authenticated properly
}
} else {
bytes memory opPubData;
if (opType == Operations.OpType.PartialExit) {
opPubData = Bytes.slice(pubData, pubdataOffset, PARTIAL_EXIT_BYTES);
} else if (opType == Operations.OpType.ForcedExit) {
opPubData = Bytes.slice(pubData, pubdataOffset, FORCED_EXIT_BYTES);
} else if (opType == Operations.OpType.WithdrawNFT) {
opPubData = Bytes.slice(pubData, pubdataOffset, WITHDRAW_NFT_BYTES);
} else if (opType == Operations.OpType.FullExit) {
opPubData = Bytes.slice(pubData, pubdataOffset, FULL_EXIT_BYTES);
Operations.FullExit memory fullExitData = Operations.readFullExitPubdata(opPubData);
checkPriorityOperation(
fullExitData,
uncommittedPriorityRequestsOffset + priorityOperationsProcessed
);
priorityOperationsProcessed++;
} else {
revert("F"); // unsupported op
}
processableOperationsHash = Utils.concatHash(processableOperationsHash, opPubData);
}
}
}
/// @notice Checks that change operation is correct
function verifyChangePubkey(bytes memory _ethWitness, Operations.ChangePubKey memory _changePk)
internal
pure
returns (bool)
{
Operations.ChangePubkeyType changePkType = Operations.ChangePubkeyType(uint8(_ethWitness[0]));
if (changePkType == Operations.ChangePubkeyType.ECRECOVER) {
return verifyChangePubkeyECRECOVER(_ethWitness, _changePk);
} else if (changePkType == Operations.ChangePubkeyType.CREATE2) {
return verifyChangePubkeyCREATE2(_ethWitness, _changePk);
} else if (changePkType == Operations.ChangePubkeyType.OldECRECOVER) {
return verifyChangePubkeyOldECRECOVER(_ethWitness, _changePk);
} else if (changePkType == Operations.ChangePubkeyType.ECRECOVERV2) {
return verifyChangePubkeyECRECOVERV2(_ethWitness, _changePk);
} else {
revert("G"); // Incorrect ChangePubKey type
}
}
/// @notice Checks that signature is valid for pubkey change message
/// @param _ethWitness Signature (65 bytes)
/// @param _changePk Parsed change pubkey operation
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
bytes32 messageHash =
keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n60",
_changePk.pubKeyHash,
_changePk.nonce,
_changePk.accountId,
bytes32(0)
)
);
address recoveredAddress = Utils.recoverAddressFromEthSignature(signature, messageHash);
return recoveredAddress == _changePk.owner && recoveredAddress != address(0);
}
/// @notice Checks that signature is valid for pubkey change message
/// @param _ethWitness Signature (65 bytes) + 32 bytes of the arbitrary signed data
/// @notice additional 32 bytes can be used to sign batches and ChangePubKey with one signature
/// @param _changePk Parsed change pubkey operation
function verifyChangePubkeyECRECOVERV2(bytes memory _ethWitness, Operations.ChangePubKey memory _changePk)
internal
pure
returns (bool)
{
(uint256 offset, bytes memory signature) = Bytes.read(_ethWitness, 1, 65); // offset is 1 because we skip type of ChangePubkey
(, bytes32 additionalData) = Bytes.readBytes32(_ethWitness, offset);
bytes32 messageHash =
keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n60",
_changePk.pubKeyHash,
_changePk.nonce,
_changePk.accountId,
additionalData
)
);
address recoveredAddress = Utils.recoverAddressFromEthSignature(signature, messageHash);
return recoveredAddress == _changePk.owner && recoveredAddress != address(0);
}
/// @notice Checks that signature is valid for pubkey change message, old version differs by form of the signed message.
/// @param _ethWitness Signature (65 bytes)
/// @param _changePk Parsed change pubkey operation
function verifyChangePubkeyOldECRECOVER(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
bytes32 messageHash =
keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n152",
"Register zkSync pubkey:\n\n",
Bytes.bytesToHexASCIIBytes(abi.encodePacked(_changePk.pubKeyHash)),
"\n",
"nonce: 0x",
Bytes.bytesToHexASCIIBytes(Bytes.toBytesFromUInt32(_changePk.nonce)),
"\n",
"account id: 0x",
Bytes.bytesToHexASCIIBytes(Bytes.toBytesFromUInt32(_changePk.accountId)),
"\n\n",
"Only sign this message for a trusted client!"
)
);
address recoveredAddress = Utils.recoverAddressFromEthSignature(signature, messageHash);
return recoveredAddress == _changePk.owner && recoveredAddress != address(0);
}
/// @notice Checks that signature is valid for pubkey change message
/// @param _ethWitness Create2 deployer address, saltArg, codeHash
/// @param _changePk Parsed change pubkey operation
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
return recoveredAddress == _changePk.owner && _changePk.nonce == 0;
}
/// @dev Creates block commitment from its data
/// @dev _offsetCommitment - hash of the array where 1 is stored in chunk where onchainOperation begins and 0 for other chunks
function createBlockCommitment(
StoredBlockInfo memory _previousBlock,
CommitBlockInfo memory _newBlockData,
bytes memory _offsetCommitment
) internal view returns (bytes32 commitment) {
bytes32 hash = sha256(abi.encodePacked(uint256(_newBlockData.blockNumber), uint256(_newBlockData.feeAccount)));
hash = sha256(abi.encodePacked(hash, _previousBlock.stateHash));
hash = sha256(abi.encodePacked(hash, _newBlockData.newStateHash));
hash = sha256(abi.encodePacked(hash, uint256(_newBlockData.timestamp)));
bytes memory pubdata = abi.encodePacked(_newBlockData.publicData, _offsetCommitment);
/// The code below is equivalent to `commitment = sha256(abi.encodePacked(hash, _publicData))`
/// We use inline assembly instead of this concise and readable code in order to avoid copying of `_publicData` (which saves ~90 gas per transfer operation).
/// Specifically, we perform the following trick:
/// First, replace the first 32 bytes of `_publicData` (where normally its length is stored) with the value of `hash`.
/// Then, we call `sha256` precompile passing the `_publicData` pointer and the length of the concatenated byte buffer.
/// Finally, we put the `_publicData.length` back to its original location (to the first word of `_publicData`).
assembly {
let hashResult := mload(0x40)
let pubDataLen := mload(pubdata)
mstore(pubdata, hash)
// staticcall to the sha256 precompile at address 0x2
let success := staticcall(gas(), 0x2, pubdata, add(pubDataLen, 0x20), hashResult, 0x20)
mstore(pubdata, pubDataLen)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
commitment := mload(hashResult)
}
}
/// @notice Checks that deposit is same as operation in priority queue
/// @param _deposit Deposit data
/// @param _priorityRequestId Operation's id in priority queue
function checkPriorityOperation(Operations.Deposit memory _deposit, uint64 _priorityRequestId) internal view {
Operations.OpType priorReqType = priorityRequests[_priorityRequestId].opType;
require(priorReqType == Operations.OpType.Deposit, "H"); // incorrect priority op type
bytes20 hashedPubdata = priorityRequests[_priorityRequestId].hashedPubData;
require(Operations.checkDepositInPriorityQueue(_deposit, hashedPubdata), "I");
}
/// @notice Checks that FullExit is same as operation in priority queue
/// @param _fullExit FullExit data
/// @param _priorityRequestId Operation's id in priority queue
function checkPriorityOperation(Operations.FullExit memory _fullExit, uint64 _priorityRequestId) internal view {
Operations.OpType priorReqType = priorityRequests[_priorityRequestId].opType;
require(priorReqType == Operations.OpType.FullExit, "J"); // incorrect priority op type
bytes20 hashedPubdata = priorityRequests[_priorityRequestId].hashedPubData;
require(Operations.checkFullExitInPriorityQueue(_fullExit, hashedPubdata), "K");
}
// 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
function addPriorityRequest(Operations.OpType _opType, bytes memory _pubData) internal {
// Expiration block is: current block number + priority expiration delta
uint64 expirationBlock = uint64(block.number + PRIORITY_EXPIRATION);
uint64 nextPriorityRequestId = firstPriorityRequestId + totalOpenPriorityRequests;
bytes20 hashedPubData = Utils.hashBytesToBytes20(_pubData);
priorityRequests[nextPriorityRequestId] = PriorityOperation({
hashedPubData: hashedPubData,
expirationBlock: expirationBlock,
opType: _opType
});
emit NewPriorityRequest(msg.sender, nextPriorityRequestId, _opType, _pubData, uint256(expirationBlock));
totalOpenPriorityRequests++;
}
function increaseBalanceToWithdraw(bytes22 _packedBalanceKey, uint128 _amount) internal {
uint128 balance = pendingBalances[_packedBalanceKey].balanceToWithdraw;
pendingBalances[_packedBalanceKey] = PendingBalance(balance.add(_amount), FILLED_GAS_RESERVE_VALUE);
}
/// @notice Sends ETH
/// @param _to Address of recipient
/// @param _amount Amount of tokens to transfer
/// @return bool flag indicating that transfer is successful
function sendETHNoRevert(address payable _to, uint256 _amount) internal returns (bool) {
(bool callSuccess, ) = _to.call{gas: WITHDRAWAL_GAS_LIMIT, value: _amount}("");
return callSuccess;
}
/// @notice Delegates the call to the additional part of the main contract.
/// @notice Should be only use to delegate the external calls as it passes the calldata
/// @notice All functions delegated to additional contract should NOT be nonReentrant
function delegateAdditional() internal {
address _target = address(additionalZkSync);
assembly {
// The pointer to the free memory slot
let ptr := mload(0x40)
// Copy function signature and arguments from calldata at zero position into memory at pointer position
calldatacopy(ptr, 0x0, calldatasize())
// Delegatecall method of the implementation contract, returns 0 on error
let result := delegatecall(gas(), _target, ptr, calldatasize(), 0x0, 0)
// Get the size of the last return data
let size := returndatasize()
// Copy the size length of bytes from return data at zero position to pointer position
returndatacopy(ptr, 0x0, size)
// Depending on result value
switch result
case 0 {
// End execution and revert state changes
revert(ptr, size)
}
default {
// Return data with length of size at pointers position
return(ptr, size)
}
}
}
}pragma solidity ^0.7.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 slot conflict
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.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when 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.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "14");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "v");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "15");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "x");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "y");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when 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.
*/
library SafeMathUInt128 {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint128 a, uint128 b) internal pure returns (uint128) {
uint128 c = a + b;
require(c >= a, "12");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint128 a, uint128 b) internal pure returns (uint128) {
return sub(a, b, "aa");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(
uint128 a,
uint128 b,
string memory errorMessage
) internal pure returns (uint128) {
require(b <= a, errorMessage);
uint128 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint128 a, uint128 b) internal pure returns (uint128) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint128 c = a * b;
require(c / a == b, "13");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint128 a, uint128 b) internal pure returns (uint128) {
return div(a, b, "ac");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(
uint128 a,
uint128 b,
string memory errorMessage
) internal pure returns (uint128) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint128 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint128 a, uint128 b) internal pure returns (uint128) {
return mod(a, b, "ad");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(
uint128 a,
uint128 b,
string memory errorMessage
) internal pure returns (uint128) {
require(b != 0, errorMessage);
return a % b;
}
}pragma solidity ^0.7.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.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./IERC20.sol";
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 Sends tokens
/// @dev NOTE: this function handles tokens that have transfer function not strictly compatible with ERC20 standard
/// @dev NOTE: call `transfer` to this token may return (bool) or nothing
/// @param _token Token address
/// @param _to Address of recipient
/// @param _amount Amount of tokens to transfer
/// @return bool flag indicating that transfer is successful
function sendERC20(
IERC20 _token,
address _to,
uint256 _amount
) internal returns (bool) {
(bool callSuccess, bytes memory callReturnValueEncoded) =
address(_token).call(abi.encodeWithSignature("transfer(address,uint256)", _to, _amount));
// `transfer` method may return (bool) or nothing.
bool returnedSuccess = callReturnValueEncoded.length == 0 || abi.decode(callReturnValueEncoded, (bool));
return callSuccess && returnedSuccess;
}
/// @notice Transfers token from one address to another
/// @dev NOTE: this function handles tokens that have transfer function not strictly compatible with ERC20 standard
/// @dev NOTE: call `transferFrom` to this token may return (bool) or nothing
/// @param _token Token address
/// @param _from Address of sender
/// @param _to Address of recipient
/// @param _amount Amount of tokens to transfer
/// @return bool flag indicating that transfer is successful
function transferFromERC20(
IERC20 _token,
address _from,
address _to,
uint256 _amount
) internal returns (bool) {
(bool callSuccess, bytes memory callReturnValueEncoded) =
address(_token).call(abi.encodeWithSignature("transferFrom(address,address,uint256)", _from, _to, _amount));
// `transferFrom` method may return (bool) or nothing.
bool returnedSuccess = callReturnValueEncoded.length == 0 || abi.decode(callReturnValueEncoded, (bool));
return callSuccess && returnedSuccess;
}
/// @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, "P"); // 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;
}
function hashBytesToBytes20(bytes memory _bytes) internal pure returns (bytes20) {
return bytes20(uint160(uint256(keccak256(_bytes))));
}
}pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
// solhint-disable max-states-count
import "./IERC20.sol";
import "./Governance.sol";
import "./Verifier.sol";
import "./Operations.sol";
import "./NFTFactory.sol";
import "./AdditionalZkSync.sol";
/// @title zkSync storage contract
/// @author Matter Labs
contract Storage {
/// @dev Flag indicates that upgrade preparation status is active
/// @dev Will store false in case of not active upgrade mode
bool internal upgradePreparationActive;
/// @dev Upgrade preparation activation timestamp (as seconds since unix epoch)
/// @dev Will be equal to zero in case of not active upgrade mode
uint256 internal upgradePreparationActivationTime;
/// @dev Verifier contract. Used to verify block proof and exit proof
Verifier internal verifier;
/// @dev Governance contract. Contains the governor (the owner) of whole system, validators list, possible tokens list
Governance internal governance;
uint8 internal constant FILLED_GAS_RESERVE_VALUE = 0xff; // we use it to set gas revert value so slot will not be emptied with 0 balance
struct PendingBalance {
uint128 balanceToWithdraw;
uint8 gasReserveValue; // gives user opportunity to fill storage slot with nonzero value
}
/// @dev Root-chain balances (per owner and token id, see packAddressAndTokenId) to withdraw
mapping(bytes22 => PendingBalance) internal pendingBalances;
// @dev Pending withdrawals are not used in this version
struct PendingWithdrawalDEPRECATED {
address to;
uint16 tokenId;
}
mapping(uint32 => PendingWithdrawalDEPRECATED) internal pendingWithdrawalsDEPRECATED;
uint32 internal firstPendingWithdrawalIndexDEPRECATED;
uint32 internal numberOfPendingWithdrawalsDEPRECATED;
/// @dev Total number of executed blocks i.e. blocks[totalBlocksExecuted] points at the latest executed block (block 0 is genesis)
uint32 public totalBlocksExecuted;
/// @notice Total number of committed blocks i.e. blocks[totalBlocksCommitted] points at the latest committed block
uint32 public totalBlocksCommitted;
/// @Old rollup block stored data - not used in current version
/// @member validator Block producer
/// @member committedAtBlock ETH block number at which this block was committed
/// @member cumulativeOnchainOperations Total number of operations in this and all previous blocks
/// @member priorityOperations Total number of priority operations for this block
/// @member commitment Hash of the block circuit commitment
/// @member stateRoot New tree root hash
///
/// Consider memory alignment when changing field order: https://solidity.readthedocs.io/en/v0.4.21/miscellaneous.html
struct BlockDEPRECATED {
uint32 committedAtBlock;
uint64 priorityOperations;
uint32 chunks;
bytes32 withdrawalsDataHash; // can be restricted to 16 bytes to reduce number of required storage slots
bytes32 commitment;
bytes32 stateRoot;
}
mapping(uint32 => BlockDEPRECATED) internal blocksDEPRECATED;
/// @dev Flag indicates that a user has exited in the exodus mode certain token balance (per account id and tokenId)
mapping(uint32 => mapping(uint32 => bool)) internal performedExodus;
/// @dev Flag indicates that exodus (mass exit) mode is triggered
/// @dev Once it was raised, it can not be cleared again, and all users must exit
bool public exodusMode;
/// @dev User authenticated fact hashes for some nonce.
mapping(address => mapping(uint32 => bytes32)) public authFacts;
/// @notice Old Priority Operation container
/// @member opType Priority operation type
/// @member pubData Priority operation public data
/// @member expirationBlock Expiration block number (ETH block) for this request (must be satisfied before)
struct PriorityOperationDEPRECATED {
Operations.OpType opType;
bytes pubData;
uint256 expirationBlock;
}
/// @dev Priority Requests mapping (request id - operation)
/// @dev Contains op type, pubdata and expiration block of unsatisfied requests.
/// @dev Numbers are in order of requests receiving
mapping(uint64 => PriorityOperationDEPRECATED) internal priorityRequestsDEPRECATED;
/// @dev First open priority request id
uint64 public firstPriorityRequestId;
/// @dev Total number of requests
uint64 public totalOpenPriorityRequests;
/// @dev 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 internal totalCommittedPriorityRequests;
/// @notice Packs address and token id into single word to use as a key in balances mapping
function packAddressAndTokenId(address _address, uint16 _tokenId) internal pure returns (bytes22) {
return bytes22((uint176(_address) | (uint176(_tokenId) << 160)));
}
/// @Rollup block stored data
/// @member blockNumber Rollup block number
/// @member priorityOperations Number of priority operations processed
/// @member pendingOnchainOperationsHash Hash of all operations that must be processed after verify
/// @member timestamp Rollup block timestamp, have the same format as Ethereum block constant
/// @member stateHash Root hash of the rollup state
/// @member commitment Verified input for the zkSync circuit
struct StoredBlockInfo {
uint32 blockNumber;
uint64 priorityOperations;
bytes32 pendingOnchainOperationsHash;
uint256 timestamp;
bytes32 stateHash;
bytes32 commitment;
}
/// @notice Returns the keccak hash of the ABI-encoded StoredBlockInfo
function hashStoredBlockInfo(StoredBlockInfo memory _storedBlockInfo) internal pure returns (bytes32) {
return keccak256(abi.encode(_storedBlockInfo));
}
/// @dev Stored hashed StoredBlockInfo for some block number
mapping(uint32 => bytes32) public storedBlockHashes;
/// @dev Total blocks proven.
uint32 public totalBlocksProven;
/// @notice Priority Operation container
/// @member hashedPubData Hashed priority operation public data
/// @member expirationBlock Expiration block number (ETH block) for this request (must be satisfied before)
/// @member opType Priority operation type
struct PriorityOperation {
bytes20 hashedPubData;
uint64 expirationBlock;
Operations.OpType opType;
}
/// @dev Priority Requests mapping (request id - operation)
/// @dev Contains op type, pubdata and expiration block of unsatisfied requests.
/// @dev Numbers are in order of requests receiving
mapping(uint64 => PriorityOperation) internal priorityRequests;
/// @dev Timer for authFacts entry reset (address, nonce -> timer).
/// @dev Used when user wants to reset `authFacts` for some nonce.
mapping(address => mapping(uint32 => uint256)) internal authFactsResetTimer;
mapping(uint32 => address) internal withdrawnNFTs;
mapping(uint32 => Operations.WithdrawNFT) internal pendingWithdrawnNFTs;
AdditionalZkSync internal additionalZkSync;
/// @dev Upgrade notice period, possibly shorten by the security council
uint256 internal approvedUpgradeNoticePeriod;
/// @dev Upgrade start timestamp (as seconds since unix epoch)
/// @dev Will be equal to zero in case of not active upgrade mode
uint256 internal upgradeStartTimestamp;
/// @dev Stores boolean flags which means the confirmations of the upgrade for each member of security council
/// @dev Will store zeroes in case of not active upgrade mode
mapping(uint256 => bool) internal securityCouncilApproves;
uint256 internal numberOfApprovalsFromSecurityCouncil;
/// @notice Checks that current state not is exodus mode
function requireActive() internal view {
require(!exodusMode, "L"); // exodus mode activated
}
}pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/// @title zkSync configuration constants
/// @author Matter Labs
contract Config {
/// @dev ERC20 tokens and ETH withdrawals gas limit, used only for complete withdrawals
uint256 internal constant WITHDRAWAL_GAS_LIMIT = 100000;
/// @dev NFT withdrawals gas limit, used only for complete withdrawals
uint256 internal constant WITHDRAWAL_NFT_GAS_LIMIT = 300000;
/// @dev Bytes in one chunk
uint8 internal constant CHUNK_BYTES = 10;
/// @dev zkSync address length
uint8 internal constant ADDRESS_BYTES = 20;
uint8 internal constant PUBKEY_HASH_BYTES = 20;
/// @dev Public key bytes length
uint8 internal constant PUBKEY_BYTES = 32;
/// @dev Ethereum signature r/s bytes length
uint8 internal constant ETH_SIGN_RS_BYTES = 32;
/// @dev Success flag bytes length
uint8 internal constant SUCCESS_FLAG_BYTES = 1;
/// @dev Max amount of tokens registered in the network (excluding ETH, which is hardcoded as tokenId = 0)
uint16 internal constant MAX_AMOUNT_OF_REGISTERED_TOKENS = 1023;
/// @dev Max account id that could be registered in the network
uint32 internal constant MAX_ACCOUNT_ID = 16777215;
/// @dev Expected average period of block creation
uint256 internal constant BLOCK_PERIOD = 15 seconds;
/// @dev ETH blocks verification expectation
/// @dev Blocks can be reverted if they are not verified for at least EXPECT_VERIFICATION_IN.
/// @dev If set to 0 validator can revert blocks at any time.
uint256 internal constant EXPECT_VERIFICATION_IN = 0 hours / BLOCK_PERIOD;
uint256 internal constant NOOP_BYTES = 1 * CHUNK_BYTES;
uint256 internal constant DEPOSIT_BYTES = 6 * CHUNK_BYTES;
uint256 internal constant MINT_NFT_BYTES = 5 * CHUNK_BYTES;
uint256 internal constant TRANSFER_TO_NEW_BYTES = 6 * CHUNK_BYTES;
uint256 internal constant PARTIAL_EXIT_BYTES = 6 * CHUNK_BYTES;
uint256 internal constant TRANSFER_BYTES = 2 * CHUNK_BYTES;
uint256 internal constant FORCED_EXIT_BYTES = 6 * CHUNK_BYTES;
uint256 internal constant WITHDRAW_NFT_BYTES = 10 * CHUNK_BYTES;
/// @dev Full exit operation length
uint256 internal constant FULL_EXIT_BYTES = 11 * CHUNK_BYTES;
/// @dev ChangePubKey operation length
uint256 internal constant CHANGE_PUBKEY_BYTES = 6 * 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 = 3 days;
/// @dev Expiration delta for priority request to be satisfied (in ETH blocks)
uint256 internal constant PRIORITY_EXPIRATION =
PRIORITY_EXPIRATION_PERIOD/BLOCK_PERIOD;
/// @dev Maximum number of priority request to clear during verifying the block
/// @dev Cause deleting storage slots cost 5k gas per each slot it's unprofitable to clear too many slots
/// @dev Value based on the assumption of ~750k gas cost of verifying and 5 used storage slots per PriorityOperation structure
uint64 internal constant MAX_PRIORITY_REQUESTS_TO_DELETE_IN_VERIFY = 6;
/// @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 = 9 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 =
MASS_FULL_EXIT_PERIOD+PRIORITY_EXPIRATION_PERIOD+TIME_TO_WITHDRAW_FUNDS_FROM_FULL_EXIT;
/// @dev Timestamp - seconds since unix epoch
uint256 internal constant COMMIT_TIMESTAMP_NOT_OLDER = 24 hours;
/// @dev Maximum available error between real commit block timestamp and analog used in the verifier (in seconds)
/// @dev Must be used cause miner's `block.timestamp` value can differ on some small value (as we know - 15 seconds)
uint256 internal constant COMMIT_TIMESTAMP_APPROXIMATION_DELTA = 15 minutes;
/// @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;
uint32 internal constant SPECIAL_ACCOUNT_ID = 16777215;
address internal constant SPECIAL_ACCOUNT_ADDRESS = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);
uint32 internal constant SPECIAL_NFT_TOKEN_ID = 2147483646;
uint32 internal constant MAX_FUNGIBLE_TOKEN_ID = 65535;
uint256 internal constant SECURITY_COUNCIL_MEMBERS_NUMBER = 15;
}pragma solidity ^0.7.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 verified
event BlockVerification(uint32 indexed blockNumber);
/// @notice Event emitted when user funds are withdrawn from the zkSync state and contract
event Withdrawal(uint16 indexed tokenId, uint128 amount);
/// @notice Event emitted when user funds are withdrawn from the zkSync state but not from contract
event WithdrawalPending(uint16 indexed tokenId, uint128 amount);
/// @notice Event emitted when user NFT is withdrawn from the zkSync state and contract
event WithdrawalNFT(uint32 indexed tokenId);
/// @notice Event emitted when user NFT is withdrawn from the zkSync state but not from contract
event WithdrawalNFTPending(uint32 indexed tokenId);
/// @notice Event emitted when user funds are deposited to the zkSync contract
event Deposit(uint16 indexed tokenId, uint128 amount);
/// @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 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 Deposit committed event.
event DepositCommit(
uint32 indexed zkSyncBlockId,
uint32 indexed accountId,
address owner,
uint16 indexed tokenId,
uint128 amount
);
/// @notice Full exit committed event.
event FullExitCommit(
uint32 indexed zkSyncBlockId,
uint32 indexed accountId,
address owner,
uint16 indexed tokenId,
uint128 amount
);
/// @notice Notice period changed
event NoticePeriodChange(uint256 newNoticePeriod);
}
/// @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 preparation status event
event PreparationStart(uint256 indexed versionId);
/// @notice Upgrade mode complete event
event UpgradeComplete(uint256 indexed versionId, address[] newTargets);
}pragma solidity ^0.7.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(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.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./Bytes.sol";
import "./Utils.sol";
/// @title zkSync operations tools
library Operations {
// Circuit ops and their pubdata (chunks * bytes)
/// @notice zkSync circuit operation type
enum OpType {
Noop,
Deposit,
TransferToNew,
PartialExit,
_CloseAccount, // used for correct op id offset
Transfer,
FullExit,
ChangePubKey,
ForcedExit,
MintNFT,
WithdrawNFT,
Swap
}
// Byte lengths
uint8 internal constant OP_TYPE_BYTES = 1;
uint8 internal constant TOKEN_BYTES = 4;
uint8 internal constant PUBKEY_BYTES = 32;
uint8 internal constant NONCE_BYTES = 4;
uint8 internal constant PUBKEY_HASH_BYTES = 20;
uint8 internal constant ADDRESS_BYTES = 20;
uint8 internal constant CONTENT_HASH_BYTES = 32;
/// @dev Packed fee bytes lengths
uint8 internal constant FEE_BYTES = 2;
/// @dev zkSync account id bytes lengths
uint8 internal constant ACCOUNT_ID_BYTES = 4;
/// @dev zkSync nft serial id bytes lengths
uint8 internal constant NFT_SERIAL_ID_BYTES = 4;
uint8 internal constant AMOUNT_BYTES = 16;
/// @dev Signature (for example full exit signature) bytes length
uint8 internal constant SIGNATURE_BYTES = 64;
// Deposit pubdata
struct Deposit {
// uint8 opType
uint32 accountId;
uint32 tokenId;
uint128 amount;
address owner;
}
uint256 internal constant PACKED_DEPOSIT_PUBDATA_BYTES =
OP_TYPE_BYTES + ACCOUNT_ID_BYTES + TOKEN_BYTES + AMOUNT_BYTES + ADDRESS_BYTES;
/// 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.accountId) = Bytes.readUInt32(_data, offset); // accountId
(offset, parsed.tokenId) = Bytes.readUInt32(_data, offset); // tokenId
(offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
(offset, parsed.owner) = Bytes.readAddress(_data, offset); // owner
require(offset == PACKED_DEPOSIT_PUBDATA_BYTES, "N"); // reading invalid deposit pubdata size
}
/// Serialize deposit pubdata
function writeDepositPubdataForPriorityQueue(Deposit memory op) internal pure returns (bytes memory buf) {
buf = abi.encodePacked(
uint8(OpType.Deposit),
bytes4(0), // accountId (ignored) (update when ACCOUNT_ID_BYTES is changed)
op.tokenId, // tokenId
op.amount, // amount
op.owner // owner
);
}
/// @notice Write deposit pubdata for priority queue check.
function checkDepositInPriorityQueue(Deposit memory op, bytes20 hashedPubdata) internal pure returns (bool) {
return Utils.hashBytesToBytes20(writeDepositPubdataForPriorityQueue(op)) == hashedPubdata;
}
// FullExit pubdata
struct FullExit {
// uint8 opType
uint32 accountId;
address owner;
uint32 tokenId;
uint128 amount;
uint32 nftCreatorAccountId;
address nftCreatorAddress;
uint32 nftSerialId;
bytes32 nftContentHash;
}
uint256 public constant PACKED_FULL_EXIT_PUBDATA_BYTES =
OP_TYPE_BYTES +
ACCOUNT_ID_BYTES +
ADDRESS_BYTES +
TOKEN_BYTES +
AMOUNT_BYTES +
ACCOUNT_ID_BYTES +
ADDRESS_BYTES +
NFT_SERIAL_ID_BYTES +
CONTENT_HASH_BYTES;
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.accountId) = Bytes.readUInt32(_data, offset); // accountId
(offset, parsed.owner) = Bytes.readAddress(_data, offset); // owner
(offset, parsed.tokenId) = Bytes.readUInt32(_data, offset); // tokenId
(offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
(offset, parsed.nftCreatorAccountId) = Bytes.readUInt32(_data, offset); // nftCreatorAccountId
(offset, parsed.nftCreatorAddress) = Bytes.readAddress(_data, offset); // nftCreatorAddress
(offset, parsed.nftSerialId) = Bytes.readUInt32(_data, offset); // nftSerialId
(offset, parsed.nftContentHash) = Bytes.readBytes32(_data, offset); // nftContentHash
require(offset == PACKED_FULL_EXIT_PUBDATA_BYTES, "O"); // reading invalid full exit pubdata size
}
function writeFullExitPubdataForPriorityQueue(FullExit memory op) internal pure returns (bytes memory buf) {
buf = abi.encodePacked(
uint8(OpType.FullExit),
op.accountId, // accountId
op.owner, // owner
op.tokenId, // tokenId
uint128(0), // amount -- ignored
uint32(0), // nftCreatorAccountId -- ignored
address(0), // nftCreatorAddress -- ignored
uint32(0), // nftSerialId -- ignored
bytes32(0) // nftContentHash -- ignored
);
}
function checkFullExitInPriorityQueue(FullExit memory op, bytes20 hashedPubdata) internal pure returns (bool) {
return Utils.hashBytesToBytes20(writeFullExitPubdataForPriorityQueue(op)) == hashedPubdata;
}
// PartialExit pubdata
struct PartialExit {
//uint8 opType; -- present in pubdata, ignored at serialization
//uint32 accountId; -- present in pubdata, ignored at serialization
uint32 tokenId;
uint128 amount;
//uint16 fee; -- present in pubdata, ignored at serialization
address owner;
}
function readPartialExitPubdata(bytes memory _data) internal pure returns (PartialExit 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 + ACCOUNT_ID_BYTES; // opType + accountId (ignored)
(offset, parsed.tokenId) = Bytes.readUInt32(_data, offset); // tokenId
(offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
offset += FEE_BYTES; // fee (ignored)
(offset, parsed.owner) = Bytes.readAddress(_data, offset); // owner
}
// ForcedExit pubdata
struct ForcedExit {
//uint8 opType; -- present in pubdata, ignored at serialization
//uint32 initiatorAccountId; -- present in pubdata, ignored at serialization
//uint32 targetAccountId; -- present in pubdata, ignored at serialization
uint32 tokenId;
uint128 amount;
//uint16 fee; -- present in pubdata, ignored at serialization
address target;
}
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 + ACCOUNT_ID_BYTES * 2; // opType + initiatorAccountId + targetAccountId (ignored)
(offset, parsed.tokenId) = Bytes.readUInt32(_data, offset); // tokenId
(offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
offset += FEE_BYTES; // fee (ignored)
(offset, parsed.target) = Bytes.readAddress(_data, offset); // target
}
// ChangePubKey
enum ChangePubkeyType {ECRECOVER, CREATE2, OldECRECOVER, ECRECOVERV2}
struct ChangePubKey {
// uint8 opType; -- present in pubdata, ignored at serialization
uint32 accountId;
bytes20 pubKeyHash;
address owner;
uint32 nonce;
//uint32 tokenId; -- present in pubdata, ignored at serialization
//uint16 fee; -- present in pubdata, ignored at serialization
}
function readChangePubKeyPubdata(bytes memory _data) internal pure returns (ChangePubKey memory parsed) {
uint256 offset = OP_TYPE_BYTES;
(offset, parsed.accountId) = Bytes.readUInt32(_data, offset); // accountId
(offset, parsed.pubKeyHash) = Bytes.readBytes20(_data, offset); // pubKeyHash
(offset, parsed.owner) = Bytes.readAddress(_data, offset); // owner
(offset, parsed.nonce) = Bytes.readUInt32(_data, offset); // nonce
}
struct WithdrawNFT {
//uint8 opType; -- present in pubdata, ignored at serialization
//uint32 accountId; -- present in pubdata, ignored at serialization
uint32 creatorAccountId;
address creatorAddress;
uint32 serialId;
bytes32 contentHash;
address receiver;
uint32 tokenId;
//uint32 feeTokenId;
//uint16 fee; -- present in pubdata, ignored at serialization
}
function readWithdrawNFTPubdata(bytes memory _data) internal pure returns (WithdrawNFT memory parsed) {
uint256 offset = OP_TYPE_BYTES + ACCOUNT_ID_BYTES; // opType + accountId (ignored)
(offset, parsed.creatorAccountId) = Bytes.readUInt32(_data, offset);
(offset, parsed.creatorAddress) = Bytes.readAddress(_data, offset);
(offset, parsed.serialId) = Bytes.readUInt32(_data, offset);
(offset, parsed.contentHash) = Bytes.readBytes32(_data, offset);
(offset, parsed.receiver) = Bytes.readAddress(_data, offset);
(offset, parsed.tokenId) = Bytes.readUInt32(_data, offset);
}
}pragma solidity ^0.7.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 Notifies contract that notice period started
function upgradeNoticePeriodStarted() external;
/// @notice Notifies contract that upgrade preparation status is activated
function upgradePreparationStarted() external;
/// @notice Notifies contract that upgrade canceled
function upgradeCanceled() external;
/// @notice Notifies contract that upgrade finishes
function upgradeFinishes() external;
/// @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.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./Ownable.sol";
import "./Config.sol";
/// @title Regenesis Multisig contract
/// @author Matter Labs
contract RegenesisMultisig is Ownable, Config {
event CandidateAccepted(bytes32 oldRootHash, bytes32 newRootHash);
event CandidateApproval(uint256 currentApproval);
bytes32 public oldRootHash;
bytes32 public newRootHash;
bytes32 public candidateOldRootHash;
bytes32 public candidateNewRootHash;
/// @dev Stores boolean flags which means the confirmations of the upgrade for each member of security council
mapping(uint256 => bool) internal securityCouncilApproves;
uint256 internal numberOfApprovalsFromSecurityCouncil;
uint256 securityCouncilThreshold;
constructor(uint256 threshold) Ownable(msg.sender) {
securityCouncilThreshold = threshold;
}
function submitHash(bytes32 _oldRootHash, bytes32 _newRootHash) external {
// Only zkSync team can submit the hashes
require(msg.sender == getMaster(), "1");
candidateOldRootHash = _oldRootHash;
candidateNewRootHash = _newRootHash;
oldRootHash = bytes32(0);
newRootHash = bytes32(0);
for (uint256 i = 0; i < SECURITY_COUNCIL_MEMBERS_NUMBER; ++i) {
securityCouncilApproves[i] = false;
}
numberOfApprovalsFromSecurityCouncil = 0;
}
function approveHash(bytes32 _oldRootHash, bytes32 _newRootHash) external {
require(_oldRootHash == candidateOldRootHash, "2");
require(_newRootHash == candidateNewRootHash, "3");
address payable[SECURITY_COUNCIL_MEMBERS_NUMBER] memory SECURITY_COUNCIL_MEMBERS =
[0xa2602ea835E03fb39CeD30B43d6b6EAf6aDe1769,0x9D5d6D4BaCCEDf6ECE1883456AA785dc996df607,0x002A5dc50bbB8d5808e418Aeeb9F060a2Ca17346,0x71E805aB236c945165b9Cd0bf95B9f2F0A0488c3,0x76C6cE74EAb57254E785d1DcC3f812D274bCcB11,0xFBfF3FF69D65A9103Bf4fdBf988f5271D12B3190,0xAfC2F2D803479A2AF3A72022D54cc0901a0ec0d6,0x4d1E3089042Ab3A93E03CA88B566b99Bd22438C6,0x19eD6cc20D44e5cF4Bb4894F50162F72402d8567,0x39415255619783A2E71fcF7d8f708A951d92e1b6,0x399a6a13D298CF3F41a562966C1a450136Ea52C2,0xee8AE1F1B4B1E1956C8Bda27eeBCE54Cf0bb5eaB,0xe7CCD4F3feA7df88Cf9B59B30f738ec1E049231f,0xA093284c707e207C36E3FEf9e0B6325fd9d0e33B,0x225d3822De44E58eE935440E0c0B829C4232086e];
for (uint256 id = 0; id < SECURITY_COUNCIL_MEMBERS_NUMBER; ++id) {
if (SECURITY_COUNCIL_MEMBERS[id] == msg.sender) {
require(securityCouncilApproves[id] == false);
securityCouncilApproves[id] = true;
numberOfApprovalsFromSecurityCouncil++;
emit CandidateApproval(numberOfApprovalsFromSecurityCouncil);
// It is ok to check for strict equality since the numberOfApprovalsFromSecurityCouncil
// is increased by one at a time. It is better to do so not to emit the
// CandidateAccepted event more than once
if (numberOfApprovalsFromSecurityCouncil == securityCouncilThreshold) {
oldRootHash = candidateOldRootHash;
newRootHash = candidateNewRootHash;
emit CandidateAccepted(oldRootHash, newRootHash);
}
}
}
}
}pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./ReentrancyGuard.sol";
import "./SafeMath.sol";
import "./SafeMathUInt128.sol";
import "./SafeCast.sol";
import "./Utils.sol";
import "./Storage.sol";
import "./Config.sol";
import "./Events.sol";
import "./Bytes.sol";
import "./Operations.sol";
import "./UpgradeableMaster.sol";
/// @title zkSync additional main contract
/// @author Matter Labs
contract AdditionalZkSync is Storage, Config, Events, ReentrancyGuard {
using SafeMath for uint256;
using SafeMathUInt128 for uint128;
function increaseBalanceToWithdraw(bytes22 _packedBalanceKey, uint128 _amount) internal {
uint128 balance = pendingBalances[_packedBalanceKey].balanceToWithdraw;
pendingBalances[_packedBalanceKey] = PendingBalance(balance.add(_amount), FILLED_GAS_RESERVE_VALUE);
}
/// @notice Withdraws token from ZkSync to root chain in case of exodus mode. User must provide proof that he owns funds
/// @param _storedBlockInfo Last verified block
/// @param _owner Owner of the account
/// @param _accountId Id of the account in the tree
/// @param _proof Proof
/// @param _tokenId Verified token id
/// @param _amount Amount for owner (must be total amount, not part of it)
function performExodus(
StoredBlockInfo memory _storedBlockInfo,
address _owner,
uint32 _accountId,
uint32 _tokenId,
uint128 _amount,
uint32 _nftCreatorAccountId,
address _nftCreatorAddress,
uint32 _nftSerialId,
bytes32 _nftContentHash,
uint256[] memory _proof
) external {
require(_accountId <= MAX_ACCOUNT_ID, "e");
require(_accountId != SPECIAL_ACCOUNT_ID, "v");
require(_tokenId < SPECIAL_NFT_TOKEN_ID, "T");
require(exodusMode, "s"); // must be in exodus mode
require(!performedExodus[_accountId][_tokenId], "t"); // already exited
require(storedBlockHashes[totalBlocksExecuted] == hashStoredBlockInfo(_storedBlockInfo), "u"); // incorrect stored block info
bool proofCorrect =
verifier.verifyExitProof(
_storedBlockInfo.stateHash,
_accountId,
_owner,
_tokenId,
_amount,
_nftCreatorAccountId,
_nftCreatorAddress,
_nftSerialId,
_nftContentHash,
_proof
);
require(proofCorrect, "x");
if (_tokenId <= MAX_FUNGIBLE_TOKEN_ID) {
bytes22 packedBalanceKey = packAddressAndTokenId(_owner, uint16(_tokenId));
increaseBalanceToWithdraw(packedBalanceKey, _amount);
emit WithdrawalPending(uint16(_tokenId), _amount);
} else {
require(_amount != 0, "Z"); // Unsupported nft amount
Operations.WithdrawNFT memory withdrawNftOp =
Operations.WithdrawNFT(
_nftCreatorAccountId,
_nftCreatorAddress,
_nftSerialId,
_nftContentHash,
_owner,
_tokenId
);
pendingWithdrawnNFTs[_tokenId] = withdrawNftOp;
emit WithdrawalNFTPending(_tokenId);
}
performedExodus[_accountId][_tokenId] = true;
}
function cancelOutstandingDepositsForExodusMode(uint64 _n, bytes[] memory _depositsPubdata) external {
require(exodusMode, "8"); // exodus mode not active
uint64 toProcess = Utils.minU64(totalOpenPriorityRequests, _n);
require(toProcess > 0, "9"); // no deposits to process
uint64 currentDepositIdx = 0;
for (uint64 id = firstPriorityRequestId; id < firstPriorityRequestId + toProcess; id++) {
if (priorityRequests[id].opType == Operations.OpType.Deposit) {
bytes memory depositPubdata = _depositsPubdata[currentDepositIdx];
require(Utils.hashBytesToBytes20(depositPubdata) == priorityRequests[id].hashedPubData, "a");
++currentDepositIdx;
Operations.Deposit memory op = Operations.readDepositPubdata(depositPubdata);
bytes22 packedBalanceKey = packAddressAndTokenId(op.owner, uint16(op.tokenId));
pendingBalances[packedBalanceKey].balanceToWithdraw += op.amount;
}
delete priorityRequests[id];
}
firstPriorityRequestId += toProcess;
totalOpenPriorityRequests -= toProcess;
}
uint256 internal constant SECURITY_COUNCIL_2_WEEKS_THRESHOLD = 8;
uint256 internal constant SECURITY_COUNCIL_1_WEEK_THRESHOLD = 10;
uint256 internal constant SECURITY_COUNCIL_3_DAYS_THRESHOLD = 12;
function cutUpgradeNoticePeriod() external {
requireActive();
address payable[SECURITY_COUNCIL_MEMBERS_NUMBER] memory SECURITY_COUNCIL_MEMBERS =
[0xa2602ea835E03fb39CeD30B43d6b6EAf6aDe1769,0x9D5d6D4BaCCEDf6ECE1883456AA785dc996df607,0x002A5dc50bbB8d5808e418Aeeb9F060a2Ca17346,0x71E805aB236c945165b9Cd0bf95B9f2F0A0488c3,0x76C6cE74EAb57254E785d1DcC3f812D274bCcB11,0xFBfF3FF69D65A9103Bf4fdBf988f5271D12B3190,0xAfC2F2D803479A2AF3A72022D54cc0901a0ec0d6,0x4d1E3089042Ab3A93E03CA88B566b99Bd22438C6,0x19eD6cc20D44e5cF4Bb4894F50162F72402d8567,0x39415255619783A2E71fcF7d8f708A951d92e1b6,0x399a6a13D298CF3F41a562966C1a450136Ea52C2,0xee8AE1F1B4B1E1956C8Bda27eeBCE54Cf0bb5eaB,0xe7CCD4F3feA7df88Cf9B59B30f738ec1E049231f,0xA093284c707e207C36E3FEf9e0B6325fd9d0e33B,0x225d3822De44E58eE935440E0c0B829C4232086e];
for (uint256 id = 0; id < SECURITY_COUNCIL_MEMBERS_NUMBER; ++id) {
if (SECURITY_COUNCIL_MEMBERS[id] == msg.sender) {
require(upgradeStartTimestamp != 0);
require(securityCouncilApproves[id] == false);
securityCouncilApproves[id] = true;
numberOfApprovalsFromSecurityCouncil++;
if (numberOfApprovalsFromSecurityCouncil == SECURITY_COUNCIL_2_WEEKS_THRESHOLD) {
if (approvedUpgradeNoticePeriod > 2 weeks) {
approvedUpgradeNoticePeriod = 2 weeks;
emit NoticePeriodChange(approvedUpgradeNoticePeriod);
}
} else if (numberOfApprovalsFromSecurityCouncil == SECURITY_COUNCIL_1_WEEK_THRESHOLD) {
if (approvedUpgradeNoticePeriod > 1 weeks) {
approvedUpgradeNoticePeriod = 1 weeks;
emit NoticePeriodChange(approvedUpgradeNoticePeriod);
}
} else if (numberOfApprovalsFromSecurityCouncil == SECURITY_COUNCIL_3_DAYS_THRESHOLD) {
if (approvedUpgradeNoticePeriod > 3 days) {
approvedUpgradeNoticePeriod = 3 days;
emit NoticePeriodChange(approvedUpgradeNoticePeriod);
}
}
break;
}
}
}
/// @notice Set data for changing pubkey hash using onchain authorization.
/// Transaction author (msg.sender) should be L2 account address
/// @notice New pubkey hash can be reset, to do that user should send two transactions:
/// 1) First `setAuthPubkeyHash` transaction for already used `_nonce` will set timer.
/// 2) After `AUTH_FACT_RESET_TIMELOCK` time is passed second `setAuthPubkeyHash` transaction will reset pubkey hash for `_nonce`.
/// @param _pubkeyHash New pubkey hash
/// @param _nonce Nonce of the change pubkey L2 transaction
function setAuthPubkeyHash(bytes calldata _pubkeyHash, uint32 _nonce) external {
requireActive();
require(_pubkeyHash.length == PUBKEY_HASH_BYTES, "y"); // PubKeyHash should be 20 bytes.
if (authFacts[msg.sender][_nonce] == bytes32(0)) {
authFacts[msg.sender][_nonce] = keccak256(_pubkeyHash);
} else {
uint256 currentResetTimer = authFactsResetTimer[msg.sender][_nonce];
if (currentResetTimer == 0) {
authFactsResetTimer[msg.sender][_nonce] = block.timestamp;
} else {
require(block.timestamp.sub(currentResetTimer) >= AUTH_FACT_RESET_TIMELOCK, "z");
authFactsResetTimer[msg.sender][_nonce] = 0;
authFacts[msg.sender][_nonce] = keccak256(_pubkeyHash);
}
}
}
/// @notice Reverts unverified blocks
function revertBlocks(StoredBlockInfo[] memory _blocksToRevert) external {
requireActive();
governance.requireActiveValidator(msg.sender);
uint32 blocksCommitted = totalBlocksCommitted;
uint32 blocksToRevert = Utils.minU32(uint32(_blocksToRevert.length), blocksCommitted - totalBlocksExecuted);
uint64 revertedPriorityRequests = 0;
for (uint32 i = 0; i < blocksToRevert; ++i) {
StoredBlockInfo memory storedBlockInfo = _blocksToRevert[i];
require(storedBlockHashes[blocksCommitted] == hashStoredBlockInfo(storedBlockInfo), "r"); // incorrect stored block info
delete storedBlockHashes[blocksCommitted];
--blocksCommitted;
revertedPriorityRequests += storedBlockInfo.priorityOperations;
}
totalBlocksCommitted = blocksCommitted;
totalCommittedPriorityRequests -= revertedPriorityRequests;
if (totalBlocksCommitted < totalBlocksProven) {
totalBlocksProven = totalBlocksCommitted;
}
emit BlocksRevert(totalBlocksExecuted, blocksCommitted);
}
}pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface IERC20 {
/**
* @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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @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);
}pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./Config.sol";
import "./Utils.sol";
import "./NFTFactory.sol";
import "./TokenGovernance.sol";
/// @title Governance Contract
/// @author Matter Labs
contract Governance is Config {
/// @notice Token added to Franklin net
event NewToken(address indexed token, uint16 indexed tokenId);
/// @notice Default nft factory has set
event SetDefaultNFTFactory(address indexed factory);
/// @notice NFT factory registered new creator account
event NFTFactoryRegisteredCreator(
uint32 indexed creatorAccountId,
address indexed creatorAddress,
address factoryAddress
);
/// @notice Governor changed
event NewGovernor(address newGovernor);
/// @notice Token Governance changed
event NewTokenGovernance(TokenGovernance newTokenGovernance);
/// @notice Validator's status changed
event ValidatorStatusUpdate(address indexed validatorAddress, bool isActive);
event TokenPausedUpdate(address indexed token, bool paused);
/// @notice Address which will exercise governance over the network i.e. add tokens, change validator set, conduct upgrades
address public networkGovernor;
/// @notice Total number of ERC20 tokens registered in the network (excluding ETH, which is hardcoded as tokenId = 0)
uint16 public totalTokens;
/// @notice List of registered tokens by tokenId
mapping(uint16 => address) public tokenAddresses;
/// @notice List of registered tokens by address
mapping(address => uint16) public tokenIds;
/// @notice List of permitted validators
mapping(address => bool) public validators;
/// @notice Paused tokens list, deposits are impossible to create for paused tokens
mapping(uint16 => bool) public pausedTokens;
/// @notice Address that is authorized to add tokens to the Governance.
TokenGovernance public tokenGovernance;
/// @notice NFT Creator address to factory address mapping
mapping(uint32 => mapping(address => NFTFactory)) public nftFactories;
/// @notice Address which will be used if NFT token has no factories
NFTFactory public defaultFactory;
/// @notice Governance contract initialization. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param initializationParameters Encoded representation of initialization parameters:
/// _networkGovernor The address of network governor
function initialize(bytes calldata initializationParameters) external {
address _networkGovernor = abi.decode(initializationParameters, (address));
networkGovernor = _networkGovernor;
}
/// @notice Governance contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param upgradeParameters Encoded representation of upgrade parameters
// solhint-disable-next-line no-empty-blocks
function upgrade(bytes calldata upgradeParameters) external {}
/// @notice Change current governor
/// @param _newGovernor Address of the new governor
function changeGovernor(address _newGovernor) external {
requireGovernor(msg.sender);
if (networkGovernor != _newGovernor) {
networkGovernor = _newGovernor;
emit NewGovernor(_newGovernor);
}
}
/// @notice Change current token governance
/// @param _newTokenGovernance Address of the new token governor
function changeTokenGovernance(TokenGovernance _newTokenGovernance) external {
requireGovernor(msg.sender);
if (tokenGovernance != _newTokenGovernance) {
tokenGovernance = _newTokenGovernance;
emit NewTokenGovernance(_newTokenGovernance);
}
}
/// @notice Add token to the list of networks tokens
/// @param _token Token address
function addToken(address _token) external {
require(msg.sender == address(tokenGovernance), "1E");
require(tokenIds[_token] == 0, "1e"); // token exists
require(totalTokens < MAX_AMOUNT_OF_REGISTERED_TOKENS, "1f"); // no free identifiers for tokens
totalTokens++;
uint16 newTokenId = totalTokens; // it is not `totalTokens - 1` because tokenId = 0 is reserved for eth
tokenAddresses[newTokenId] = _token;
tokenIds[_token] = newTokenId;
emit NewToken(_token, newTokenId);
}
/// @notice Pause token deposits for the given token
/// @param _tokenAddr Token address
/// @param _tokenPaused Token paused status
function setTokenPaused(address _tokenAddr, bool _tokenPaused) external {
requireGovernor(msg.sender);
uint16 tokenId = this.validateTokenAddress(_tokenAddr);
if (pausedTokens[tokenId] != _tokenPaused) {
pausedTokens[tokenId] = _tokenPaused;
emit TokenPausedUpdate(_tokenAddr, _tokenPaused);
}
}
/// @notice Change validator status (active or not active)
/// @param _validator Validator address
/// @param _active Active flag
function setValidator(address _validator, bool _active) external {
requireGovernor(msg.sender);
if (validators[_validator] != _active) {
validators[_validator] = _active;
emit ValidatorStatusUpdate(_validator, _active);
}
}
/// @notice Check if specified address is is governor
/// @param _address Address to check
function requireGovernor(address _address) public view {
require(_address == networkGovernor, "1g"); // only by governor
}
/// @notice Checks if validator is active
/// @param _address Validator address
function requireActiveValidator(address _address) external view {
require(validators[_address], "1h"); // validator is not active
}
/// @notice Validate token id (must be less than or equal to total tokens amount)
/// @param _tokenId Token id
/// @return bool flag that indicates if token id is less than or equal to total tokens amount
function isValidTokenId(uint16 _tokenId) external view returns (bool) {
return _tokenId <= totalTokens;
}
/// @notice Validate token address
/// @param _tokenAddr Token address
/// @return tokens id
function validateTokenAddress(address _tokenAddr) external view returns (uint16) {
uint16 tokenId = tokenIds[_tokenAddr];
require(tokenId != 0, "1i"); // 0 is not a valid token
return tokenId;
}
function packRegisterNFTFactoryMsg(
uint32 _creatorAccountId,
address _creatorAddress,
address _factoryAddress
) internal pure returns (bytes memory) {
return
abi.encodePacked(
"\x19Ethereum Signed Message:\n141",
"\nCreator's account ID in zkSync: ",
Bytes.bytesToHexASCIIBytes(abi.encodePacked((_creatorAccountId))),
"\nCreator: ",
Bytes.bytesToHexASCIIBytes(abi.encodePacked((_creatorAddress))),
"\nFactory: ",
Bytes.bytesToHexASCIIBytes(abi.encodePacked((_factoryAddress)))
);
}
/// @notice Register creator corresponding to the factory
/// @param _creatorAccountId Creator's zkSync account ID
/// @param _creatorAddress NFT creator address
/// @param _signature Creator's signature
function registerNFTFactoryCreator(
uint32 _creatorAccountId,
address _creatorAddress,
bytes memory _signature
) external {
require(address(nftFactories[_creatorAccountId][_creatorAddress]) == address(0), "Q");
bytes32 messageHash = keccak256(packRegisterNFTFactoryMsg(_creatorAccountId, _creatorAddress, msg.sender));
address recoveredAddress = Utils.recoverAddressFromEthSignature(_signature, messageHash);
require(recoveredAddress == _creatorAddress && recoveredAddress != address(0), "ws");
nftFactories[_creatorAccountId][_creatorAddress] = NFTFactory(msg.sender);
emit NFTFactoryRegisteredCreator(_creatorAccountId, _creatorAddress, msg.sender);
}
//@notice Set default factory for our contract. This factory will be used to mint an NFT token that has no factory
//@param _factory Address of NFT factory
function setDefaultNFTFactory(address _factory) external {
requireGovernor(msg.sender);
require(address(_factory) != address(0), "mb1"); // Factory should be non zero
require(address(defaultFactory) == address(0), "mb2"); // NFTFactory is already set
defaultFactory = NFTFactory(_factory);
emit SetDefaultNFTFactory(_factory);
}
function getNFTFactory(uint32 _creatorAccountId, address _creatorAddress) external view returns (NFTFactory) {
NFTFactory _factory = nftFactories[_creatorAccountId][_creatorAddress];
if (address(_factory) == address(0)) {
require(address(defaultFactory) != address(0), "fs"); // NFTFactory does not set
return defaultFactory;
} else {
return _factory;
}
}
}pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./KeysWithPlonkVerifier.sol";
import "./Config.sol";
// Hardcoded constants to avoid accessing store
contract Verifier is KeysWithPlonkVerifier, KeysWithPlonkVerifierOld, Config {
// solhint-disable-next-line no-empty-blocks
function initialize(bytes calldata) external {}
/// @notice Verifier contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param upgradeParameters Encoded representation of upgrade parameters
// solhint-disable-next-line no-empty-blocks
function upgrade(bytes calldata upgradeParameters) external {}
function verifyAggregatedBlockProof(
uint256[] memory _recursiveInput,
uint256[] memory _proof,
uint8[] memory _vkIndexes,
uint256[] memory _individualVksInputs,
uint256[16] memory _subproofsLimbs
) external view returns (bool) {
for (uint256 i = 0; i < _individualVksInputs.length; ++i) {
uint256 commitment = _individualVksInputs[i];
_individualVksInputs[i] = commitment & INPUT_MASK;
}
VerificationKey memory vk = getVkAggregated(uint32(_vkIndexes.length));
return
verify_serialized_proof_with_recursion(
_recursiveInput,
_proof,
VK_TREE_ROOT,
VK_MAX_INDEX,
_vkIndexes,
_individualVksInputs,
_subproofsLimbs,
vk
);
}
function verifyExitProof(
bytes32 _rootHash,
uint32 _accountId,
address _owner,
uint32 _tokenId,
uint128 _amount,
uint32 _nftCreatorAccountId,
address _nftCreatorAddress,
uint32 _nftSerialId,
bytes32 _nftContentHash,
uint256[] calldata _proof
) external view returns (bool) {
bytes32 commitment =
sha256(
abi.encodePacked(
_rootHash,
_accountId,
_owner,
_tokenId,
_amount,
_nftCreatorAccountId,
_nftCreatorAddress,
_nftSerialId,
_nftContentHash
)
);
uint256[] memory inputs = new uint256[](1);
inputs[0] = uint256(commitment) & INPUT_MASK;
ProofOld memory proof = deserialize_proof_old(inputs, _proof);
VerificationKeyOld memory vk = getVkExit();
require(vk.num_inputs == inputs.length, "n1");
return verify_old(proof, vk);
}
}pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
interface NFTFactory {
function mintNFTFromZkSync(
address creator,
address recipient,
uint32 creatorAccountId,
uint32 serialId,
bytes32 contentHash,
// Even though the token id can fit into the uint32, we still use
// the uint256 to preserve consistency with the ERC721 parent contract
uint256 tokenId
) external;
event MintNFTFromZkSync(
address indexed creator,
address indexed recipient,
uint32 creatorAccountId,
uint32 serialId,
bytes32 contentHash,
uint256 tokenId
);
}pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./Governance.sol";
import "./IERC20.sol";
import "./Utils.sol";
/// @title Token Governance Contract
/// @author Matter Labs
/// @notice Contract is used to allow anyone to add new ERC20 tokens to zkSync given sufficient payment
contract TokenGovernance {
/// @notice Token lister added or removed (see `tokenLister`)
event TokenListerUpdate(address indexed tokenLister, bool isActive);
/// @notice Listing fee token set
event ListingFeeTokenUpdate(IERC20 indexed newListingFeeToken);
/// @notice Listing fee set
event ListingFeeUpdate(uint256 newListingFee);
/// @notice Maximum number of listed tokens updated
event ListingCapUpdate(uint16 newListingCap);
/// @notice The treasury (the account which will receive the fee) was updated
event TreasuryUpdate(address newTreasury);
/// @notice zkSync governance contract
Governance public governance;
/// @notice Token used to collect listing fee for addition of new token to zkSync network
IERC20 public listingFeeToken;
/// @notice Token listing fee
uint256 public listingFee;
/// @notice Max number of tokens that can be listed using this contract
uint16 public listingCap;
/// @notice Addresses that can list tokens without fee
mapping(address => bool) public tokenLister;
/// @notice Address that collects listing payments
address public treasury;
constructor(
Governance _governance,
IERC20 _listingFeeToken,
uint256 _listingFee,
uint16 _listingCap,
address _treasury
) {
governance = _governance;
listingFeeToken = _listingFeeToken;
listingFee = _listingFee;
listingCap = _listingCap;
treasury = _treasury;
address governor = governance.networkGovernor();
// We add zkSync governor as a first token lister.
tokenLister[governor] = true;
emit TokenListerUpdate(governor, true);
}
/// @notice Adds new ERC20 token to zkSync network.
/// @notice If caller is not present in the `tokenLister` map payment of `listingFee` in `listingFeeToken` should be made.
/// @notice NOTE: before calling this function make sure to approve `listingFeeToken` transfer for this contract.
function addToken(address _token) external {
require(governance.totalTokens() < listingCap, "can't add more tokens"); // Impossible to add more tokens using this contract
if (!tokenLister[msg.sender]) {
// Collect fees
bool feeTransferOk = Utils.transferFromERC20(listingFeeToken, msg.sender, treasury, listingFee);
require(feeTransferOk, "fee transfer failed"); // Failed to receive payment for token addition.
}
governance.addToken(_token);
}
/// Governance functions (this contract is governed by zkSync governor)
/// @notice Set new listing token and fee
/// @notice Can be called only by zkSync governor
function setListingFeeToken(IERC20 _newListingFeeToken, uint256 _newListingFee) external {
governance.requireGovernor(msg.sender);
listingFeeToken = _newListingFeeToken;
listingFee = _newListingFee;
emit ListingFeeTokenUpdate(_newListingFeeToken);
}
/// @notice Set new listing fee
/// @notice Can be called only by zkSync governor
function setListingFee(uint256 _newListingFee) external {
governance.requireGovernor(msg.sender);
listingFee = _newListingFee;
emit ListingFeeUpdate(_newListingFee);
}
/// @notice Enable or disable token lister. If enabled new tokens can be added by that address without payment
/// @notice Can be called only by zkSync governor
function setLister(address _listerAddress, bool _active) external {
governance.requireGovernor(msg.sender);
if (tokenLister[_listerAddress] != _active) {
tokenLister[_listerAddress] = _active;
emit TokenListerUpdate(_listerAddress, _active);
}
}
/// @notice Change maximum amount of tokens that can be listed using this method
/// @notice Can be called only by zkSync governor
function setListingCap(uint16 _newListingCap) external {
governance.requireGovernor(msg.sender);
listingCap = _newListingCap;
emit ListingCapUpdate(_newListingCap);
}
/// @notice Change address that collects payments for listing tokens.
/// @notice Can be called only by zkSync governor
function setTreasury(address _newTreasury) external {
governance.requireGovernor(msg.sender);
treasury = _newTreasury;
emit TreasuryUpdate(_newTreasury);
}
}pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./PlonkCore.sol";
// Hardcoded constants to avoid accessing store
contract KeysWithPlonkVerifier is VerifierWithDeserialize {
uint256 constant VK_TREE_ROOT = 0x12d3ca8e7e185734779f3969f1d0a9dbb357a737ed605c37c7157c341012e6d9;
uint8 constant VK_MAX_INDEX = 3;
function getVkAggregated(uint32 _proofs) internal pure returns (VerificationKey memory vk) {
if (_proofs == uint32(1)) { return getVkAggregated1(); }
else if (_proofs == uint32(4)) { return getVkAggregated4(); }
else if (_proofs == uint32(8)) { return getVkAggregated8(); }
}
function getVkAggregated1() internal pure returns(VerificationKey memory vk) {
vk.domain_size = 4194304;
vk.num_inputs = 1;
vk.omega = PairingsBn254.new_fr(0x18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede);
vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
0x19fbd6706b4cbde524865701eae0ae6a270608a09c3afdab7760b685c1c6c41b,
0x25082a191f0690c175cc9af1106c6c323b5b5de4e24dc23be1e965e1851bca48
);
vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
0x16c02d9ca95023d1812a58d16407d1ea065073f02c916290e39242303a8a1d8e,
0x230338b422ce8533e27cd50086c28cb160cf05a7ae34ecd5899dbdf449dc7ce0
);
vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
0x1db0d133243750e1ea692050bbf6068a49dc9f6bae1f11960b6ce9e10adae0f5,
0x12a453ed0121ae05de60848b4374d54ae4b7127cb307372e14e8daf5097c5123
);
vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
0x1062ed5e86781fd34f78938e5950c2481a79f132085d2bc7566351ddff9fa3b7,
0x2fd7aac30f645293cc99883ab57d8c99a518d5b4ab40913808045e8653497346
);
vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
0x062755048bb95739f845e8659795813127283bf799443d62fea600ae23e7f263,
0x2af86098beaa241281c78a454c5d1aa6e9eedc818c96cd1e6518e1ac2d26aa39
);
vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
0x0994e25148bbd25be655034f81062d1ebf0a1c2b41e0971434beab1ae8101474,
0x27cc8cfb1fafd13068aeee0e08a272577d89f8aa0fb8507aabbc62f37587b98f
);
vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
0x044edf69ce10cfb6206795f92c3be2b0d26ab9afd3977b789840ee58c7dbe927,
0x2a8aa20c106f8dc7e849bc9698064dcfa9ed0a4050d794a1db0f13b0ee3def37
);
vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
0x136967f1a2696db05583a58dbf8971c5d9d1dc5f5c97e88f3b4822aa52fefa1c,
0x127b41299ea5c840c3b12dbe7b172380f432b7b63ce3b004750d6abb9e7b3b7a
);
vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
0x02fd5638bf3cc2901395ad1124b951e474271770a337147a2167e9797ab9d951,
0x0fcb2e56b077c8461c36911c9252008286d782e96030769bf279024fc81d412a
);
vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
0x1865c60ecad86f81c6c952445707203c9c7fdace3740232ceb704aefd5bd45b3,
0x2f35e29b39ec8bb054e2cff33c0299dd13f8c78ea24a07622128a7444aba3f26
);
vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
0x2a86ec9c6c1f903650b5abbf0337be556b03f79aecc4d917e90c7db94518dde6,
0x15b1b6be641336eebd58e7991be2991debbbd780e70c32b49225aa98d10b7016
);
vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
0x213e42fcec5297b8e01a602684fcd412208d15bdac6b6331a8819d478ba46899,
0x03223485f4e808a3b2496ae1a3c0dfbcbf4391cffc57ee01e8fca114636ead18
);
vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
0x2e9b02f8cf605ad1a36e99e990a07d435de06716448ad53053c7a7a5341f71e1,
0x2d6fdf0bc8bd89112387b1894d6f24b45dcb122c09c84344b6fc77a619dd1d59
);
vk.copy_permutation_non_residues[0] = PairingsBn254.new_fr(
0x0000000000000000000000000000000000000000000000000000000000000005
);
vk.copy_permutation_non_residues[1] = PairingsBn254.new_fr(
0x0000000000000000000000000000000000000000000000000000000000000007
);
vk.copy_permutation_non_residues[2] = PairingsBn254.new_fr(
0x000000000000000000000000000000000000000000000000000000000000000a
);
vk.g2_x = PairingsBn254.new_g2(
[0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
[0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
);
}
function getVkAggregated4() internal pure returns(VerificationKey memory vk) {
vk.domain_size = 8388608;
vk.num_inputs = 1;
vk.omega = PairingsBn254.new_fr(0x1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863);
vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
0x2988e24b15bce9a1e3a4d1d9a8f7c7a65db6c29fd4c6f4afe1a3fbd954d4b4b6,
0x0bdb6e5ba27a22e03270c7c71399b866b28d7cec504d30e665d67be58e306e12
);
vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
0x20f3d30d3a91a7419d658f8c035e42a811c9f75eac2617e65729033286d36089,
0x07ac91e8194eb78a9db537e9459dd6ca26bef8770dde54ac3dd396450b1d4cfe
);
vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
0x0311872bab6df6e9095a9afe40b12e2ed58f00cc88835442e6b4cf73fb3e147d,
0x2cdfc5b5e73737809b54644b2f96494f8fcc1dd0fb440f64f44930b432c4542d
);
vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
0x28fd545b1e960d2eff3142271affa4096ef724212031fdabe22dd4738f36472b,
0x2c743150ee9894ff3965d8f1129399a3b89a1a9289d4cfa904b0a648d3a8a9fa
);
vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
0x2c283ce950eee1173b78657e57c80658a8398e7970a9a45b20cd39aff16ad61a,
0x081c003cbd09f7c3e0d723d6ebbaf432421c188d5759f5ee8ff1ee1dc357d4a8
);
vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
0x2eb50a2dd293a71a0c038e958c5237bd7f50b2f0c9ee6385895a553de1517d43,
0x15fdc2b5b28fc351f987b98aa6caec7552cefbafa14e6651061eec4f41993b65
);
vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
0x17a9403e5c846c1ca5e767c89250113aa156fdb1f026aa0b4db59c09d06816ec,
0x2512241972ca3ee4839ac72a4cab39ddb413a7553556abd7909284b34ee73f6b
);
vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
0x09edd69c8baa7928b16615e993e3032bc8cbf9f42bfa3cf28caba1078d371edb,
0x12e5c39148af860a87b14ae938f33eafa91deeb548cda4cc23ed9ba3e6e496b8
);
vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
0x0e25c0027706ca3fd3daae849f7c50ec88d4d030da02452001dec7b554cc71b4,
0x2421da0ca385ff7ba9e5ae68890655669248c8c8187e67d12b2a7ae97e2cff8b
);
vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
0x151536359fe184567bce57379833f6fae485e5cc9bc27423d83d281aaf2701df,
0x116beb145bc27faae5a8ae30c28040d3baafb3ea47360e528227b94adb9e4f26
);
vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
0x23ee338093db23364a6e44acfb60d810a4c4bd6565b185374f7840152d3ae82c,
0x0f6714f3ee113b9dfb6b653f04bf497602588b16b96ac682d9a5dd880a0aa601
);
vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
0x05860b0ea3c6f22150812aee304bf35e1a95cfa569a8da52b42dba44a122378a,
0x19e5a9f3097289272e65e842968752c5355d1cdb2d3d737050e4dfe32ebe1e41
);
vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
0x3046881fcbe369ac6f99fea8b9505de85ded3de3bc445060be4bc6ef651fa352,
0x06fe14c1dd6c2f2b48aebeb6fd525573d276b2e148ad25e75c57a58588f755ec
);
vk.copy_permutation_non_residues[0] = PairingsBn254.new_fr(
0x0000000000000000000000000000000000000000000000000000000000000005
);
vk.copy_permutation_non_residues[1] = PairingsBn254.new_fr(
0x0000000000000000000000000000000000000000000000000000000000000007
);
vk.copy_permutation_non_residues[2] = PairingsBn254.new_fr(
0x000000000000000000000000000000000000000000000000000000000000000a
);
vk.g2_x = PairingsBn254.new_g2(
[0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
[0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
);
}
function getVkAggregated8() internal pure returns(VerificationKey memory vk) {
vk.domain_size = 16777216;
vk.num_inputs = 1;
vk.omega = PairingsBn254.new_fr(0x1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb);
vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
0x218bdb295b7207114aeea948e2d3baef158d4057812f94005d8ff54341b6ce6f,
0x1398585c039ba3cf336687301e95fbbf6b0638d31c64b1d815bb49091d0c1aad
);
vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
0x2e40b8a98e688c9e00f607a64520a850d35f277dc0b645628494337bb75870e8,
0x2da4ef753cc4869e53cff171009dbffea9166b8ffbafd17783d712278a79f13e
);
vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
0x1b638de3c6cc2e0badc48305ee3533678a45f52edf30277303551128772303a2,
0x2794c375cbebb7c28379e8abf42d529a1c291319020099935550c83796ba14ac
);
vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
0x189cd01d67b44cf2c1e10765c69adaafd6a5929952cf55732e312ecf00166956,
0x15976c99ef2c911bd3a72c9613b7fe9e66b03dd8963bfed705c96e3e88fdb1af
);
vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
0x0745a77052dc66afc61163ec3737651e5b846ca7ec7fae1853515d0f10a51bd9,
0x2bd27ecf4fb7f5053cc6de3ddb7a969fac5150a6fb5555ca917d16a7836e4c0a
);
vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
0x2787aea173d07508083893b02ea962be71c3b628d1da7d7c4db0def49f73ad8f,
0x22fdc951a97dc2ac7d8292a6c263898022f4623c643a56b9265b33c72e628886
);
vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
0x0aafe35c49634858e44e9af259cac47a6f8402eb870f9f95217dcb8a33a73e64,
0x1b47a7641a7c918784e84fc2494bfd8014ebc77069b94650d25cb5e25fbb7003
);
vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
0x11cfc3fe28dfd5d663d53ceacc5ec620da85ae5aa971f0f003f57e75cd05bf9f,
0x28b325f30984634fc46c6750f402026d4ff43e5325cbe34d35bf8ac4fc9cc533
);
vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
0x2ada816636b9447def36e35dd3ab0e3f7a8bbe3ae32a5a4904dee3fc26e58015,
0x2cd12d1a50aaadef4e19e1b1955c932e992e688c2883da862bd7fad17aae66f6
);
vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
0x20cc506f273be4d114cbf2807c14a769d03169168892e2855cdfa78c3095c89d,
0x08f99d338aee985d780d036473c624de9fd7960b2a4a7ad361c8c125cf11899e
);
vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
0x01260265d3b1167eac1030f3d04326f08a1f2bb1e026e54afec844e3729386e2,
0x16d75b53ec2552c63e84ea5f4bfe1507c3198045875457c1d9295d6699f39d56
);
vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
0x1f4d73c63d163c3f5ef1b5caa41988cacbdbca38334e8f54d7ee9bbbb622e200,
0x2f48f5f93d9845526ef0348f1c3def63cfc009645eb2a95d1746c7941e888a78
);
vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
0x1dbd386fe258366222becc570a7f6405b25ff52818b93bdd54eaa20a6b22025a,
0x2b2b4e978ac457d752f50b02609bd7d2054286b963821b2ec7cd3dd1507479fa
);
vk.copy_permutation_non_residues[0] = PairingsBn254.new_fr(
0x0000000000000000000000000000000000000000000000000000000000000005
);
vk.copy_permutation_non_residues[1] = PairingsBn254.new_fr(
0x0000000000000000000000000000000000000000000000000000000000000007
);
vk.copy_permutation_non_residues[2] = PairingsBn254.new_fr(
0x000000000000000000000000000000000000000000000000000000000000000a
);
vk.g2_x = PairingsBn254.new_g2(
[0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
[0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
);
}
}
// Hardcoded constants to avoid accessing store
contract KeysWithPlonkVerifierOld is VerifierWithDeserializeOld {
function getVkExit() internal pure returns(VerificationKeyOld memory vk) {
vk.domain_size = 524288;
vk.num_inputs = 1;
vk.omega = PairingsBn254.new_fr(0x0cf1526aaafac6bacbb67d11a4077806b123f767e4b0883d14cc0193568fc082);
vk.selector_commitments[0] = PairingsBn254.new_g1(
0x114dd473f77a15b602201577dd4b64a32a783cb32fbc02911e512df6a219695d,
0x04c68f82a5dd7d0cc90318bdff493b3d552d148ad859c373ffe55275e043c43b
);
vk.selector_commitments[1] = PairingsBn254.new_g1(
0x245e8c882af503cb5421f5135b4295a920ccf68b42ae7fb967f044f54e2aaa29,
0x071322ee387a9ce49fe7ef2edb6e9237203dee49ec47483af85e356b79fb06fd
);
vk.selector_commitments[2] = PairingsBn254.new_g1(
0x0187754ab593b07a420b3b4d215c20ed49acf90fc4c97e4b06e8f5bc0a2eb3f4,
0x0170f9286ce950286a16ea25136c163c0b32019f31b89c256a612d40b863d0b6
);
vk.selector_commitments[3] = PairingsBn254.new_g1(
0x0defecfae1d2b9ec9b2ee4d4798c625fa50f6a4ddb7747a7293df0c17fcb90c2,
0x0f91d08fceebf85fb80f12cda78cefa1ee9dbf5cfe7c4f0704b3c6620fa50c55
);
vk.selector_commitments[4] = PairingsBn254.new_g1(
0x2f7fef3b3fb64af6640f93803a18b3e5ce4e0e60aecd4f924c833fa6fa6da961,
0x03908fc737113ac7f3529fe3b36efca200c66d1d85d2fc081973214c586de732
);
vk.selector_commitments[5] = PairingsBn254.new_g1(
0x14ce3c0e9b78fc331327249e707f58fa4bb0ed746bdc9c2262ad0cf905609627,
0x09e64fdac452b424e98fc4a92f7222693d0d84ab48aadd9c46151dbe5f1a34a9
);
// we only have access to value of the d(x) witness polynomial on the next
// trace step, so we only need one element here and deal with it in other places
// by having this in mind
vk.next_step_selector_commitments[0] = PairingsBn254.new_g1(
0x1d10bfd923c17d9623ec02db00099355b373021432ae1edef69b0f5f461f78d6,
0x24e370a93f65f42888781d0158bb6ef9136c8bbd047d7993b8276bc8df8b640a
);
vk.permutation_commitments[0] = PairingsBn254.new_g1(
0x1fd1755ed4d06d91d50db4771d332cfa2bc2ca0e10ac8b77e0d6b73b993e788e,
0x0bdbf3b7f0d3cffdcf818f1fba18b90914eda59b454bd1858c6c0916b817f883
);
vk.permutation_commitments[1] = PairingsBn254.new_g1(
0x1f3b8d12ffa2ceb2bb42d232ad2cf11bce3183472b622e11cc841d26f42ad507,
0x0ce815e32b3bd14311cde210cda1bd351617d539ed3e9d96a8605f364f3a29b0
);
vk.permutation_commitments[2] = PairingsBn254.new_g1(
0x123afa8c1cec1956d7330db062498a2a3e3a9862926c02e1228d9cfb63d3c301,
0x0f5af15ff0a3e35486c541f72956b53ff6d0740384ef6463c866146c1bd2afc8
);
vk.permutation_commitments[3] = PairingsBn254.new_g1(
0x01069e38ea6396af1623921101d3d3d14ee46942fb23bf1d110efb994c3ee573,
0x232a8ce7151e69601a7867f9dcac8e2de4dd8352d119c90bbb0fb84720c02513
);
vk.permutation_non_residues[0] = PairingsBn254.new_fr(
0x0000000000000000000000000000000000000000000000000000000000000005
);
vk.permutation_non_residues[1] = PairingsBn254.new_fr(
0x0000000000000000000000000000000000000000000000000000000000000007
);
vk.permutation_non_residues[2] = PairingsBn254.new_fr(
0x000000000000000000000000000000000000000000000000000000000000000a
);
vk.g2_x = PairingsBn254.new_g2(
[0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1, 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
[0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4, 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
);
}
}pragma solidity >=0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
// solhint-disable
library PairingsBn254 {
uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 constant bn254_b_coeff = 3;
struct G1Point {
uint256 X;
uint256 Y;
}
struct Fr {
uint256 value;
}
function new_fr(uint256 fr) internal pure returns (Fr memory) {
require(fr < r_mod);
return Fr({value: fr});
}
function copy(Fr memory self) internal pure returns (Fr memory n) {
n.value = self.value;
}
function assign(Fr memory self, Fr memory other) internal pure {
self.value = other.value;
}
function inverse(Fr memory fr) internal view returns (Fr memory) {
require(fr.value != 0);
return pow(fr, r_mod - 2);
}
function add_assign(Fr memory self, Fr memory other) internal pure {
self.value = addmod(self.value, other.value, r_mod);
}
function sub_assign(Fr memory self, Fr memory other) internal pure {
self.value = addmod(self.value, r_mod - other.value, r_mod);
}
function mul_assign(Fr memory self, Fr memory other) internal pure {
self.value = mulmod(self.value, other.value, r_mod);
}
function pow(Fr memory self, uint256 power) internal view returns (Fr memory) {
uint256[6] memory input = [32, 32, 32, self.value, power, r_mod];
uint256[1] memory result;
bool success;
assembly {
success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20)
}
require(success);
return Fr({value: result[0]});
}
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
function P1() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) {
return G1Point(x, y);
}
function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
if (x == 0 && y == 0) {
// point of infinity is (0,0)
return G1Point(x, y);
}
// check encoding
require(x < q_mod);
require(y < q_mod);
// check on curve
uint256 lhs = mulmod(y, y, q_mod); // y^2
uint256 rhs = mulmod(x, x, q_mod); // x^2
rhs = mulmod(rhs, x, q_mod); // x^3
rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b
require(lhs == rhs);
return G1Point(x, y);
}
function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) {
return G2Point(x, y);
}
function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) {
result.X = self.X;
result.Y = self.Y;
}
function P2() internal pure returns (G2Point memory) {
// for some reason ethereum expects to have c1*v + c0 form
return
G2Point(
[
0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
],
[
0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
]
);
}
function negate(G1Point memory self) internal pure {
// The prime q in the base field F_q for G1
if (self.Y == 0) {
require(self.X == 0);
return;
}
self.Y = q_mod - self.Y;
}
function point_add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
point_add_into_dest(p1, p2, r);
return r;
}
function point_add_assign(G1Point memory p1, G1Point memory p2) internal view {
point_add_into_dest(p1, p2, p1);
}
function point_add_into_dest(
G1Point memory p1,
G1Point memory p2,
G1Point memory dest
) internal view {
if (p2.X == 0 && p2.Y == 0) {
// we add zero, nothing happens
dest.X = p1.X;
dest.Y = p1.Y;
return;
} else if (p1.X == 0 && p1.Y == 0) {
// we add into zero, and we add non-zero point
dest.X = p2.X;
dest.Y = p2.Y;
return;
} else {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success = false;
assembly {
success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
}
require(success);
}
}
function point_sub_assign(G1Point memory p1, G1Point memory p2) internal view {
point_sub_into_dest(p1, p2, p1);
}
function point_sub_into_dest(
G1Point memory p1,
G1Point memory p2,
G1Point memory dest
) internal view {
if (p2.X == 0 && p2.Y == 0) {
// we subtracted zero, nothing happens
dest.X = p1.X;
dest.Y = p1.Y;
return;
} else if (p1.X == 0 && p1.Y == 0) {
// we subtract from zero, and we subtract non-zero point
dest.X = p2.X;
dest.Y = q_mod - p2.Y;
return;
} else {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = q_mod - p2.Y;
bool success = false;
assembly {
success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
}
require(success);
}
}
function point_mul(G1Point memory p, Fr memory s) internal view returns (G1Point memory r) {
point_mul_into_dest(p, s, r);
return r;
}
function point_mul_assign(G1Point memory p, Fr memory s) internal view {
point_mul_into_dest(p, s, p);
}
function point_mul_into_dest(
G1Point memory p,
Fr memory s,
G1Point memory dest
) internal view {
uint256[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s.value;
bool success;
assembly {
success := staticcall(gas(), 7, input, 0x60, dest, 0x40)
}
require(success);
}
function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) {
require(p1.length == p2.length);
uint256 elements = p1.length;
uint256 inputSize = elements * 6;
uint256[] memory input = new uint256[](inputSize);
for (uint256 i = 0; i < elements; i++) {
input[i * 6 + 0] = p1[i].X;
input[i * 6 + 1] = p1[i].Y;
input[i * 6 + 2] = p2[i].X[0];
input[i * 6 + 3] = p2[i].X[1];
input[i * 6 + 4] = p2[i].Y[0];
input[i * 6 + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
assembly {
success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
}
require(success);
return out[0] != 0;
}
/// Convenience method for a pairing check for two pairs.
function pairingProd2(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2
) internal view returns (bool) {
G1Point[] memory p1 = new G1Point[](2);
G2Point[] memory p2 = new G2Point[](2);
p1[0] = a1;
p1[1] = b1;
p2[0] = a2;
p2[1] = b2;
return pairing(p1, p2);
}
}
library TranscriptLibrary {
// flip 0xe000000000000000000000000000000000000000000000000000000000000000;
uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint32 constant DST_0 = 0;
uint32 constant DST_1 = 1;
uint32 constant DST_CHALLENGE = 2;
struct Transcript {
bytes32 state_0;
bytes32 state_1;
uint32 challenge_counter;
}
function new_transcript() internal pure returns (Transcript memory t) {
t.state_0 = bytes32(0);
t.state_1 = bytes32(0);
t.challenge_counter = 0;
}
function update_with_u256(Transcript memory self, uint256 value) internal pure {
bytes32 old_state_0 = self.state_0;
self.state_0 = keccak256(abi.encodePacked(DST_0, old_state_0, self.state_1, value));
self.state_1 = keccak256(abi.encodePacked(DST_1, old_state_0, self.state_1, value));
}
function update_with_fr(Transcript memory self, PairingsBn254.Fr memory value) internal pure {
update_with_u256(self, value.value);
}
function update_with_g1(Transcript memory self, PairingsBn254.G1Point memory p) internal pure {
update_with_u256(self, p.X);
update_with_u256(self, p.Y);
}
function get_challenge(Transcript memory self) internal pure returns (PairingsBn254.Fr memory challenge) {
bytes32 query = keccak256(abi.encodePacked(DST_CHALLENGE, self.state_0, self.state_1, self.challenge_counter));
self.challenge_counter += 1;
challenge = PairingsBn254.Fr({value: uint256(query) & FR_MASK});
}
}
contract Plonk4VerifierWithAccessToDNext {
uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
using PairingsBn254 for PairingsBn254.G1Point;
using PairingsBn254 for PairingsBn254.G2Point;
using PairingsBn254 for PairingsBn254.Fr;
using TranscriptLibrary for TranscriptLibrary.Transcript;
uint256 constant ZERO = 0;
uint256 constant ONE = 1;
uint256 constant TWO = 2;
uint256 constant THREE = 3;
uint256 constant FOUR = 4;
uint256 constant STATE_WIDTH = 4;
uint256 constant NUM_DIFFERENT_GATES = 2;
uint256 constant NUM_SETUP_POLYS_FOR_MAIN_GATE = 7;
uint256 constant NUM_SETUP_POLYS_RANGE_CHECK_GATE = 0;
uint256 constant ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP = 1;
uint256 constant NUM_GATE_SELECTORS_OPENED_EXPLICITLY = 1;
uint256 constant RECURSIVE_CIRCUIT_INPUT_COMMITMENT_MASK =
0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint256 constant LIMB_WIDTH = 68;
struct VerificationKey {
uint256 domain_size;
uint256 num_inputs;
PairingsBn254.Fr omega;
PairingsBn254.G1Point[NUM_SETUP_POLYS_FOR_MAIN_GATE + NUM_SETUP_POLYS_RANGE_CHECK_GATE] gate_setup_commitments;
PairingsBn254.G1Point[NUM_DIFFERENT_GATES] gate_selector_commitments;
PairingsBn254.G1Point[STATE_WIDTH] copy_permutation_commitments;
PairingsBn254.Fr[STATE_WIDTH - 1] copy_permutation_non_residues;
PairingsBn254.G2Point g2_x;
}
struct Proof {
uint256[] input_values;
PairingsBn254.G1Point[STATE_WIDTH] wire_commitments;
PairingsBn254.G1Point copy_permutation_grand_product_commitment;
PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_commitments;
PairingsBn254.Fr[STATE_WIDTH] wire_values_at_z;
PairingsBn254.Fr[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP] wire_values_at_z_omega;
PairingsBn254.Fr[NUM_GATE_SELECTORS_OPENED_EXPLICITLY] gate_selector_values_at_z;
PairingsBn254.Fr copy_grand_product_at_z_omega;
PairingsBn254.Fr quotient_polynomial_at_z;
PairingsBn254.Fr linearization_polynomial_at_z;
PairingsBn254.Fr[STATE_WIDTH - 1] permutation_polynomials_at_z;
PairingsBn254.G1Point opening_at_z_proof;
PairingsBn254.G1Point opening_at_z_omega_proof;
}
struct PartialVerifierState {
PairingsBn254.Fr alpha;
PairingsBn254.Fr beta;
PairingsBn254.Fr gamma;
PairingsBn254.Fr v;
PairingsBn254.Fr u;
PairingsBn254.Fr z;
PairingsBn254.Fr[] cached_lagrange_evals;
}
function evaluate_lagrange_poly_out_of_domain(
uint256 poly_num,
uint256 domain_size,
PairingsBn254.Fr memory omega,
PairingsBn254.Fr memory at
) internal view returns (PairingsBn254.Fr memory res) {
require(poly_num < domain_size);
PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory omega_power = omega.pow(poly_num);
res = at.pow(domain_size);
res.sub_assign(one);
require(res.value != 0); // Vanishing polynomial can not be zero at point `at`
res.mul_assign(omega_power);
PairingsBn254.Fr memory den = PairingsBn254.copy(at);
den.sub_assign(omega_power);
den.mul_assign(PairingsBn254.new_fr(domain_size));
den = den.inverse();
res.mul_assign(den);
}
function batch_evaluate_lagrange_poly_out_of_domain(
uint256[] memory poly_nums,
uint256 domain_size,
PairingsBn254.Fr memory omega,
PairingsBn254.Fr memory at
) internal view returns (PairingsBn254.Fr[] memory res) {
PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory tmp_1 = PairingsBn254.new_fr(0);
PairingsBn254.Fr memory tmp_2 = PairingsBn254.new_fr(domain_size);
PairingsBn254.Fr memory vanishing_at_z = at.pow(domain_size);
vanishing_at_z.sub_assign(one);
// we can not have random point z be in domain
require(vanishing_at_z.value != 0);
PairingsBn254.Fr[] memory nums = new PairingsBn254.Fr[](poly_nums.length);
PairingsBn254.Fr[] memory dens = new PairingsBn254.Fr[](poly_nums.length);
// numerators in a form omega^i * (z^n - 1)
// denoms in a form (z - omega^i) * N
for (uint256 i = 0; i < poly_nums.length; i++) {
tmp_1 = omega.pow(poly_nums[i]); // power of omega
nums[i].assign(vanishing_at_z);
nums[i].mul_assign(tmp_1);
dens[i].assign(at); // (X - omega^i) * N
dens[i].sub_assign(tmp_1);
dens[i].mul_assign(tmp_2); // mul by domain size
}
PairingsBn254.Fr[] memory partial_products = new PairingsBn254.Fr[](poly_nums.length);
partial_products[0].assign(PairingsBn254.new_fr(1));
for (uint256 i = 1; i < dens.length - 1; i++) {
partial_products[i].assign(dens[i - 1]);
partial_products[i].mul_assign(dens[i]);
}
tmp_2.assign(partial_products[partial_products.length - 1]);
tmp_2.mul_assign(dens[dens.length - 1]);
tmp_2 = tmp_2.inverse(); // tmp_2 contains a^-1 * b^-1 (with! the last one)
for (uint256 i = dens.length - 1; i < dens.length; i--) {
dens[i].assign(tmp_2); // all inversed
dens[i].mul_assign(partial_products[i]); // clear lowest terms
tmp_2.mul_assign(dens[i]);
}
for (uint256 i = 0; i < nums.length; i++) {
nums[i].mul_assign(dens[i]);
}
return nums;
}
function evaluate_vanishing(uint256 domain_size, PairingsBn254.Fr memory at)
internal
view
returns (PairingsBn254.Fr memory res)
{
res = at.pow(domain_size);
res.sub_assign(PairingsBn254.new_fr(1));
}
function verify_at_z(
PartialVerifierState memory state,
Proof memory proof,
VerificationKey memory vk
) internal view returns (bool) {
PairingsBn254.Fr memory lhs = evaluate_vanishing(vk.domain_size, state.z);
require(lhs.value != 0); // we can not check a polynomial relationship if point `z` is in the domain
lhs.mul_assign(proof.quotient_polynomial_at_z);
PairingsBn254.Fr memory quotient_challenge = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory rhs = PairingsBn254.copy(proof.linearization_polynomial_at_z);
// public inputs
PairingsBn254.Fr memory tmp = PairingsBn254.new_fr(0);
PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0);
for (uint256 i = 0; i < proof.input_values.length; i++) {
tmp.assign(state.cached_lagrange_evals[i]);
tmp.mul_assign(PairingsBn254.new_fr(proof.input_values[i]));
inputs_term.add_assign(tmp);
}
inputs_term.mul_assign(proof.gate_selector_values_at_z[0]);
rhs.add_assign(inputs_term);
// now we need 5th power
quotient_challenge.mul_assign(state.alpha);
quotient_challenge.mul_assign(state.alpha);
quotient_challenge.mul_assign(state.alpha);
quotient_challenge.mul_assign(state.alpha);
quotient_challenge.mul_assign(state.alpha);
PairingsBn254.Fr memory z_part = PairingsBn254.copy(proof.copy_grand_product_at_z_omega);
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
tmp.assign(proof.permutation_polynomials_at_z[i]);
tmp.mul_assign(state.beta);
tmp.add_assign(state.gamma);
tmp.add_assign(proof.wire_values_at_z[i]);
z_part.mul_assign(tmp);
}
tmp.assign(state.gamma);
// we need a wire value of the last polynomial in enumeration
tmp.add_assign(proof.wire_values_at_z[STATE_WIDTH - 1]);
z_part.mul_assign(tmp);
z_part.mul_assign(quotient_challenge);
rhs.sub_assign(z_part);
quotient_challenge.mul_assign(state.alpha);
tmp.assign(state.cached_lagrange_evals[0]);
tmp.mul_assign(quotient_challenge);
rhs.sub_assign(tmp);
return lhs.value == rhs.value;
}
function add_contribution_from_range_constraint_gates(
PartialVerifierState memory state,
Proof memory proof,
PairingsBn254.Fr memory current_alpha
) internal pure returns (PairingsBn254.Fr memory res) {
// now add contribution from range constraint gate
// we multiply selector commitment by all the factors (alpha*(c - 4d)(c - 4d - 1)(..-2)(..-3) + alpha^2 * (4b - c)()()() + {} + {})
PairingsBn254.Fr memory one_fr = PairingsBn254.new_fr(ONE);
PairingsBn254.Fr memory two_fr = PairingsBn254.new_fr(TWO);
PairingsBn254.Fr memory three_fr = PairingsBn254.new_fr(THREE);
PairingsBn254.Fr memory four_fr = PairingsBn254.new_fr(FOUR);
res = PairingsBn254.new_fr(0);
PairingsBn254.Fr memory t0 = PairingsBn254.new_fr(0);
PairingsBn254.Fr memory t1 = PairingsBn254.new_fr(0);
PairingsBn254.Fr memory t2 = PairingsBn254.new_fr(0);
for (uint256 i = 0; i < 3; i++) {
current_alpha.mul_assign(state.alpha);
// high - 4*low
// this is 4*low
t0 = PairingsBn254.copy(proof.wire_values_at_z[3 - i]);
t0.mul_assign(four_fr);
// high
t1 = PairingsBn254.copy(proof.wire_values_at_z[2 - i]);
t1.sub_assign(t0);
// t0 is now t1 - {0,1,2,3}
// first unroll manually for -0;
t2 = PairingsBn254.copy(t1);
// -1
t0 = PairingsBn254.copy(t1);
t0.sub_assign(one_fr);
t2.mul_assign(t0);
// -2
t0 = PairingsBn254.copy(t1);
t0.sub_assign(two_fr);
t2.mul_assign(t0);
// -3
t0 = PairingsBn254.copy(t1);
t0.sub_assign(three_fr);
t2.mul_assign(t0);
t2.mul_assign(current_alpha);
res.add_assign(t2);
}
// now also d_next - 4a
current_alpha.mul_assign(state.alpha);
// high - 4*low
// this is 4*low
t0 = PairingsBn254.copy(proof.wire_values_at_z[0]);
t0.mul_assign(four_fr);
// high
t1 = PairingsBn254.copy(proof.wire_values_at_z_omega[0]);
t1.sub_assign(t0);
// t0 is now t1 - {0,1,2,3}
// first unroll manually for -0;
t2 = PairingsBn254.copy(t1);
// -1
t0 = PairingsBn254.copy(t1);
t0.sub_assign(one_fr);
t2.mul_assign(t0);
// -2
t0 = PairingsBn254.copy(t1);
t0.sub_assign(two_fr);
t2.mul_assign(t0);
// -3
t0 = PairingsBn254.copy(t1);
t0.sub_assign(three_fr);
t2.mul_assign(t0);
t2.mul_assign(current_alpha);
res.add_assign(t2);
return res;
}
function reconstruct_linearization_commitment(
PartialVerifierState memory state,
Proof memory proof,
VerificationKey memory vk
) internal view returns (PairingsBn254.G1Point memory res) {
// we compute what power of v is used as a delinearization factor in batch opening of
// commitments. Let's label W(x) = 1 / (x - z) *
// [
// t_0(x) + z^n * t_1(x) + z^2n * t_2(x) + z^3n * t_3(x) - t(z)
// + v (r(x) - r(z))
// + v^{2..5} * (witness(x) - witness(z))
// + v^{6} * (selector(x) - selector(z))
// + v^{7..9} * (permutation(x) - permutation(z))
// ]
// W'(x) = 1 / (x - z*omega) *
// [
// + v^10 (z(x) - z(z*omega)) <- we need this power
// + v^11 * (d(x) - d(z*omega))
// ]
//
// we reconstruct linearization polynomial virtual selector
// for that purpose we first linearize over main gate (over all it's selectors)
// and multiply them by value(!) of the corresponding main gate selector
res = PairingsBn254.copy_g1(vk.gate_setup_commitments[STATE_WIDTH + 1]); // index of q_const(x)
PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();
PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(0);
// addition gates
for (uint256 i = 0; i < STATE_WIDTH; i++) {
tmp_g1 = vk.gate_setup_commitments[i].point_mul(proof.wire_values_at_z[i]);
res.point_add_assign(tmp_g1);
}
// multiplication gate
tmp_fr.assign(proof.wire_values_at_z[0]);
tmp_fr.mul_assign(proof.wire_values_at_z[1]);
tmp_g1 = vk.gate_setup_commitments[STATE_WIDTH].point_mul(tmp_fr);
res.point_add_assign(tmp_g1);
// d_next
tmp_g1 = vk.gate_setup_commitments[STATE_WIDTH + 2].point_mul(proof.wire_values_at_z_omega[0]); // index of q_d_next(x)
res.point_add_assign(tmp_g1);
// multiply by main gate selector(z)
res.point_mul_assign(proof.gate_selector_values_at_z[0]); // these is only one explicitly opened selector
PairingsBn254.Fr memory current_alpha = PairingsBn254.new_fr(ONE);
// calculate scalar contribution from the range check gate
tmp_fr = add_contribution_from_range_constraint_gates(state, proof, current_alpha);
tmp_g1 = vk.gate_selector_commitments[1].point_mul(tmp_fr); // selector commitment for range constraint gate * scalar
res.point_add_assign(tmp_g1);
// proceed as normal to copy permutation
current_alpha.mul_assign(state.alpha); // alpha^5
PairingsBn254.Fr memory alpha_for_grand_product = PairingsBn254.copy(current_alpha);
// z * non_res * beta + gamma + a
PairingsBn254.Fr memory grand_product_part_at_z = PairingsBn254.copy(state.z);
grand_product_part_at_z.mul_assign(state.beta);
grand_product_part_at_z.add_assign(proof.wire_values_at_z[0]);
grand_product_part_at_z.add_assign(state.gamma);
for (uint256 i = 0; i < vk.copy_permutation_non_residues.length; i++) {
tmp_fr.assign(state.z);
tmp_fr.mul_assign(vk.copy_permutation_non_residues[i]);
tmp_fr.mul_assign(state.beta);
tmp_fr.add_assign(state.gamma);
tmp_fr.add_assign(proof.wire_values_at_z[i + 1]);
grand_product_part_at_z.mul_assign(tmp_fr);
}
grand_product_part_at_z.mul_assign(alpha_for_grand_product);
// alpha^n & L_{0}(z), and we bump current_alpha
current_alpha.mul_assign(state.alpha);
tmp_fr.assign(state.cached_lagrange_evals[0]);
tmp_fr.mul_assign(current_alpha);
grand_product_part_at_z.add_assign(tmp_fr);
// prefactor for grand_product(x) is complete
// add to the linearization a part from the term
// - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X)
PairingsBn254.Fr memory last_permutation_part_at_z = PairingsBn254.new_fr(1);
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
tmp_fr.assign(state.beta);
tmp_fr.mul_assign(proof.permutation_polynomials_at_z[i]);
tmp_fr.add_assign(state.gamma);
tmp_fr.add_assign(proof.wire_values_at_z[i]);
last_permutation_part_at_z.mul_assign(tmp_fr);
}
last_permutation_part_at_z.mul_assign(state.beta);
last_permutation_part_at_z.mul_assign(proof.copy_grand_product_at_z_omega);
last_permutation_part_at_z.mul_assign(alpha_for_grand_product); // we multiply by the power of alpha from the argument
// actually multiply prefactors by z(x) and perm_d(x) and combine them
tmp_g1 = proof.copy_permutation_grand_product_commitment.point_mul(grand_product_part_at_z);
tmp_g1.point_sub_assign(vk.copy_permutation_commitments[STATE_WIDTH - 1].point_mul(last_permutation_part_at_z));
res.point_add_assign(tmp_g1);
// multiply them by v immedately as linearization has a factor of v^1
res.point_mul_assign(state.v);
// res now contains contribution from the gates linearization and
// copy permutation part
// now we need to add a part that is the rest
// for z(x*omega):
// - (a(z) + beta*perm_a + gamma)*()*()*(d(z) + gamma) * z(x*omega)
}
function aggregate_commitments(
PartialVerifierState memory state,
Proof memory proof,
VerificationKey memory vk
) internal view returns (PairingsBn254.G1Point[2] memory res) {
PairingsBn254.G1Point memory d = reconstruct_linearization_commitment(state, proof, vk);
PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size);
PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();
PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1);
PairingsBn254.G1Point memory commitment_aggregation = PairingsBn254.copy_g1(proof.quotient_poly_commitments[0]);
PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(1);
for (uint256 i = 1; i < proof.quotient_poly_commitments.length; i++) {
tmp_fr.mul_assign(z_in_domain_size);
tmp_g1 = proof.quotient_poly_commitments[i].point_mul(tmp_fr);
commitment_aggregation.point_add_assign(tmp_g1);
}
aggregation_challenge.mul_assign(state.v);
commitment_aggregation.point_add_assign(d);
for (uint256 i = 0; i < proof.wire_commitments.length; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_g1 = proof.wire_commitments[i].point_mul(aggregation_challenge);
commitment_aggregation.point_add_assign(tmp_g1);
}
for (uint256 i = 0; i < NUM_GATE_SELECTORS_OPENED_EXPLICITLY; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_g1 = vk.gate_selector_commitments[0].point_mul(aggregation_challenge);
commitment_aggregation.point_add_assign(tmp_g1);
}
for (uint256 i = 0; i < vk.copy_permutation_commitments.length - 1; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_g1 = vk.copy_permutation_commitments[i].point_mul(aggregation_challenge);
commitment_aggregation.point_add_assign(tmp_g1);
}
aggregation_challenge.mul_assign(state.v);
// now do prefactor for grand_product(x*omega)
tmp_fr.assign(aggregation_challenge);
tmp_fr.mul_assign(state.u);
commitment_aggregation.point_add_assign(proof.copy_permutation_grand_product_commitment.point_mul(tmp_fr));
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(aggregation_challenge);
tmp_fr.mul_assign(state.u);
tmp_g1 = proof.wire_commitments[STATE_WIDTH - 1].point_mul(tmp_fr);
commitment_aggregation.point_add_assign(tmp_g1);
// collect opening values
aggregation_challenge = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory aggregated_value = PairingsBn254.copy(proof.quotient_polynomial_at_z);
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.linearization_polynomial_at_z);
tmp_fr.mul_assign(aggregation_challenge);
aggregated_value.add_assign(tmp_fr);
for (uint256 i = 0; i < proof.wire_values_at_z.length; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.wire_values_at_z[i]);
tmp_fr.mul_assign(aggregation_challenge);
aggregated_value.add_assign(tmp_fr);
}
for (uint256 i = 0; i < proof.gate_selector_values_at_z.length; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.gate_selector_values_at_z[i]);
tmp_fr.mul_assign(aggregation_challenge);
aggregated_value.add_assign(tmp_fr);
}
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.permutation_polynomials_at_z[i]);
tmp_fr.mul_assign(aggregation_challenge);
aggregated_value.add_assign(tmp_fr);
}
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.copy_grand_product_at_z_omega);
tmp_fr.mul_assign(aggregation_challenge);
tmp_fr.mul_assign(state.u);
aggregated_value.add_assign(tmp_fr);
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.wire_values_at_z_omega[0]);
tmp_fr.mul_assign(aggregation_challenge);
tmp_fr.mul_assign(state.u);
aggregated_value.add_assign(tmp_fr);
commitment_aggregation.point_sub_assign(PairingsBn254.P1().point_mul(aggregated_value));
PairingsBn254.G1Point memory pair_with_generator = commitment_aggregation;
pair_with_generator.point_add_assign(proof.opening_at_z_proof.point_mul(state.z));
tmp_fr.assign(state.z);
tmp_fr.mul_assign(vk.omega);
tmp_fr.mul_assign(state.u);
pair_with_generator.point_add_assign(proof.opening_at_z_omega_proof.point_mul(tmp_fr));
PairingsBn254.G1Point memory pair_with_x = proof.opening_at_z_omega_proof.point_mul(state.u);
pair_with_x.point_add_assign(proof.opening_at_z_proof);
pair_with_x.negate();
res[0] = pair_with_generator;
res[1] = pair_with_x;
return res;
}
function verify_initial(
PartialVerifierState memory state,
Proof memory proof,
VerificationKey memory vk
) internal view returns (bool) {
require(proof.input_values.length == vk.num_inputs);
require(vk.num_inputs >= 1);
TranscriptLibrary.Transcript memory transcript = TranscriptLibrary.new_transcript();
for (uint256 i = 0; i < vk.num_inputs; i++) {
transcript.update_with_u256(proof.input_values[i]);
}
for (uint256 i = 0; i < proof.wire_commitments.length; i++) {
transcript.update_with_g1(proof.wire_commitments[i]);
}
state.beta = transcript.get_challenge();
state.gamma = transcript.get_challenge();
transcript.update_with_g1(proof.copy_permutation_grand_product_commitment);
state.alpha = transcript.get_challenge();
for (uint256 i = 0; i < proof.quotient_poly_commitments.length; i++) {
transcript.update_with_g1(proof.quotient_poly_commitments[i]);
}
state.z = transcript.get_challenge();
uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs);
for (uint256 i = 0; i < lagrange_poly_numbers.length; i++) {
lagrange_poly_numbers[i] = i;
}
state.cached_lagrange_evals = batch_evaluate_lagrange_poly_out_of_domain(
lagrange_poly_numbers,
vk.domain_size,
vk.omega,
state.z
);
bool valid = verify_at_z(state, proof, vk);
if (valid == false) {
return false;
}
transcript.update_with_fr(proof.quotient_polynomial_at_z);
for (uint256 i = 0; i < proof.wire_values_at_z.length; i++) {
transcript.update_with_fr(proof.wire_values_at_z[i]);
}
for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
transcript.update_with_fr(proof.wire_values_at_z_omega[i]);
}
transcript.update_with_fr(proof.gate_selector_values_at_z[0]);
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
transcript.update_with_fr(proof.permutation_polynomials_at_z[i]);
}
transcript.update_with_fr(proof.copy_grand_product_at_z_omega);
transcript.update_with_fr(proof.linearization_polynomial_at_z);
state.v = transcript.get_challenge();
transcript.update_with_g1(proof.opening_at_z_proof);
transcript.update_with_g1(proof.opening_at_z_omega_proof);
state.u = transcript.get_challenge();
return true;
}
// This verifier is for a PLONK with a state width 4
// and main gate equation
// q_a(X) * a(X) +
// q_b(X) * b(X) +
// q_c(X) * c(X) +
// q_d(X) * d(X) +
// q_m(X) * a(X) * b(X) +
// q_constants(X)+
// q_d_next(X) * d(X*omega)
// where q_{}(X) are selectors a, b, c, d - state (witness) polynomials
// q_d_next(X) "peeks" into the next row of the trace, so it takes
// the same d(X) polynomial, but shifted
function aggregate_for_verification(Proof memory proof, VerificationKey memory vk)
internal
view
returns (bool valid, PairingsBn254.G1Point[2] memory part)
{
PartialVerifierState memory state;
valid = verify_initial(state, proof, vk);
if (valid == false) {
return (valid, part);
}
part = aggregate_commitments(state, proof, vk);
(valid, part);
}
function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) {
(bool valid, PairingsBn254.G1Point[2] memory recursive_proof_part) = aggregate_for_verification(proof, vk);
if (valid == false) {
return false;
}
valid = PairingsBn254.pairingProd2(
recursive_proof_part[0],
PairingsBn254.P2(),
recursive_proof_part[1],
vk.g2_x
);
return valid;
}
function verify_recursive(
Proof memory proof,
VerificationKey memory vk,
uint256 recursive_vks_root,
uint8 max_valid_index,
uint8[] memory recursive_vks_indexes,
uint256[] memory individual_vks_inputs,
uint256[16] memory subproofs_limbs
) internal view returns (bool) {
(uint256 recursive_input, PairingsBn254.G1Point[2] memory aggregated_g1s) =
reconstruct_recursive_public_input(
recursive_vks_root,
max_valid_index,
recursive_vks_indexes,
individual_vks_inputs,
subproofs_limbs
);
assert(recursive_input == proof.input_values[0]);
(bool valid, PairingsBn254.G1Point[2] memory recursive_proof_part) = aggregate_for_verification(proof, vk);
if (valid == false) {
return false;
}
// aggregated_g1s = inner
// recursive_proof_part = outer
PairingsBn254.G1Point[2] memory combined = combine_inner_and_outer(aggregated_g1s, recursive_proof_part);
valid = PairingsBn254.pairingProd2(combined[0], PairingsBn254.P2(), combined[1], vk.g2_x);
return valid;
}
function combine_inner_and_outer(PairingsBn254.G1Point[2] memory inner, PairingsBn254.G1Point[2] memory outer)
internal
view
returns (PairingsBn254.G1Point[2] memory result)
{
// reuse the transcript primitive
TranscriptLibrary.Transcript memory transcript = TranscriptLibrary.new_transcript();
transcript.update_with_g1(inner[0]);
transcript.update_with_g1(inner[1]);
transcript.update_with_g1(outer[0]);
transcript.update_with_g1(outer[1]);
PairingsBn254.Fr memory challenge = transcript.get_challenge();
// 1 * inner + challenge * outer
result[0] = PairingsBn254.copy_g1(inner[0]);
result[1] = PairingsBn254.copy_g1(inner[1]);
PairingsBn254.G1Point memory tmp = outer[0].point_mul(challenge);
result[0].point_add_assign(tmp);
tmp = outer[1].point_mul(challenge);
result[1].point_add_assign(tmp);
return result;
}
function reconstruct_recursive_public_input(
uint256 recursive_vks_root,
uint8 max_valid_index,
uint8[] memory recursive_vks_indexes,
uint256[] memory individual_vks_inputs,
uint256[16] memory subproofs_aggregated
) internal pure returns (uint256 recursive_input, PairingsBn254.G1Point[2] memory reconstructed_g1s) {
assert(recursive_vks_indexes.length == individual_vks_inputs.length);
bytes memory concatenated = abi.encodePacked(recursive_vks_root);
uint8 index;
for (uint256 i = 0; i < recursive_vks_indexes.length; i++) {
index = recursive_vks_indexes[i];
assert(index <= max_valid_index);
concatenated = abi.encodePacked(concatenated, index);
}
uint256 input;
for (uint256 i = 0; i < recursive_vks_indexes.length; i++) {
input = individual_vks_inputs[i];
assert(input < r_mod);
concatenated = abi.encodePacked(concatenated, input);
}
concatenated = abi.encodePacked(concatenated, subproofs_aggregated);
bytes32 commitment = sha256(concatenated);
recursive_input = uint256(commitment) & RECURSIVE_CIRCUIT_INPUT_COMMITMENT_MASK;
reconstructed_g1s[0] = PairingsBn254.new_g1_checked(
subproofs_aggregated[0] +
(subproofs_aggregated[1] << LIMB_WIDTH) +
(subproofs_aggregated[2] << (2 * LIMB_WIDTH)) +
(subproofs_aggregated[3] << (3 * LIMB_WIDTH)),
subproofs_aggregated[4] +
(subproofs_aggregated[5] << LIMB_WIDTH) +
(subproofs_aggregated[6] << (2 * LIMB_WIDTH)) +
(subproofs_aggregated[7] << (3 * LIMB_WIDTH))
);
reconstructed_g1s[1] = PairingsBn254.new_g1_checked(
subproofs_aggregated[8] +
(subproofs_aggregated[9] << LIMB_WIDTH) +
(subproofs_aggregated[10] << (2 * LIMB_WIDTH)) +
(subproofs_aggregated[11] << (3 * LIMB_WIDTH)),
subproofs_aggregated[12] +
(subproofs_aggregated[13] << LIMB_WIDTH) +
(subproofs_aggregated[14] << (2 * LIMB_WIDTH)) +
(subproofs_aggregated[15] << (3 * LIMB_WIDTH))
);
return (recursive_input, reconstructed_g1s);
}
}
contract VerifierWithDeserialize is Plonk4VerifierWithAccessToDNext {
uint256 constant SERIALIZED_PROOF_LENGTH = 34;
function deserialize_proof(uint256[] memory public_inputs, uint256[] memory serialized_proof)
internal
pure
returns (Proof memory proof)
{
require(serialized_proof.length == SERIALIZED_PROOF_LENGTH);
proof.input_values = new uint256[](public_inputs.length);
for (uint256 i = 0; i < public_inputs.length; i++) {
proof.input_values[i] = public_inputs[i];
}
uint256 j = 0;
for (uint256 i = 0; i < STATE_WIDTH; i++) {
proof.wire_commitments[i] = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
j += 2;
}
proof.copy_permutation_grand_product_commitment = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j + 1]
);
j += 2;
for (uint256 i = 0; i < STATE_WIDTH; i++) {
proof.quotient_poly_commitments[i] = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j + 1]
);
j += 2;
}
for (uint256 i = 0; i < STATE_WIDTH; i++) {
proof.wire_values_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
}
for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
proof.wire_values_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
}
for (uint256 i = 0; i < proof.gate_selector_values_at_z.length; i++) {
proof.gate_selector_values_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
}
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
proof.permutation_polynomials_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
}
proof.copy_grand_product_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
proof.quotient_polynomial_at_z = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
proof.linearization_polynomial_at_z = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
proof.opening_at_z_proof = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
j += 2;
proof.opening_at_z_omega_proof = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
}
function verify_serialized_proof(
uint256[] memory public_inputs,
uint256[] memory serialized_proof,
VerificationKey memory vk
) public view returns (bool) {
require(vk.num_inputs == public_inputs.length);
Proof memory proof = deserialize_proof(public_inputs, serialized_proof);
bool valid = verify(proof, vk);
return valid;
}
function verify_serialized_proof_with_recursion(
uint256[] memory public_inputs,
uint256[] memory serialized_proof,
uint256 recursive_vks_root,
uint8 max_valid_index,
uint8[] memory recursive_vks_indexes,
uint256[] memory individual_vks_inputs,
uint256[16] memory subproofs_limbs,
VerificationKey memory vk
) public view returns (bool) {
require(vk.num_inputs == public_inputs.length);
Proof memory proof = deserialize_proof(public_inputs, serialized_proof);
bool valid =
verify_recursive(
proof,
vk,
recursive_vks_root,
max_valid_index,
recursive_vks_indexes,
individual_vks_inputs,
subproofs_limbs
);
return valid;
}
}
contract Plonk4VerifierWithAccessToDNextOld {
using PairingsBn254 for PairingsBn254.G1Point;
using PairingsBn254 for PairingsBn254.G2Point;
using PairingsBn254 for PairingsBn254.Fr;
using TranscriptLibrary for TranscriptLibrary.Transcript;
uint256 constant STATE_WIDTH_OLD = 4;
uint256 constant ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP_OLD = 1;
struct VerificationKeyOld {
uint256 domain_size;
uint256 num_inputs;
PairingsBn254.Fr omega;
PairingsBn254.G1Point[STATE_WIDTH_OLD + 2] selector_commitments; // STATE_WIDTH for witness + multiplication + constant
PairingsBn254.G1Point[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP_OLD] next_step_selector_commitments;
PairingsBn254.G1Point[STATE_WIDTH_OLD] permutation_commitments;
PairingsBn254.Fr[STATE_WIDTH_OLD - 1] permutation_non_residues;
PairingsBn254.G2Point g2_x;
}
struct ProofOld {
uint256[] input_values;
PairingsBn254.G1Point[STATE_WIDTH_OLD] wire_commitments;
PairingsBn254.G1Point grand_product_commitment;
PairingsBn254.G1Point[STATE_WIDTH_OLD] quotient_poly_commitments;
PairingsBn254.Fr[STATE_WIDTH_OLD] wire_values_at_z;
PairingsBn254.Fr[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP_OLD] wire_values_at_z_omega;
PairingsBn254.Fr grand_product_at_z_omega;
PairingsBn254.Fr quotient_polynomial_at_z;
PairingsBn254.Fr linearization_polynomial_at_z;
PairingsBn254.Fr[STATE_WIDTH_OLD - 1] permutation_polynomials_at_z;
PairingsBn254.G1Point opening_at_z_proof;
PairingsBn254.G1Point opening_at_z_omega_proof;
}
struct PartialVerifierStateOld {
PairingsBn254.Fr alpha;
PairingsBn254.Fr beta;
PairingsBn254.Fr gamma;
PairingsBn254.Fr v;
PairingsBn254.Fr u;
PairingsBn254.Fr z;
PairingsBn254.Fr[] cached_lagrange_evals;
}
function evaluate_lagrange_poly_out_of_domain_old(
uint256 poly_num,
uint256 domain_size,
PairingsBn254.Fr memory omega,
PairingsBn254.Fr memory at
) internal view returns (PairingsBn254.Fr memory res) {
require(poly_num < domain_size);
PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory omega_power = omega.pow(poly_num);
res = at.pow(domain_size);
res.sub_assign(one);
require(res.value != 0); // Vanishing polynomial can not be zero at point `at`
res.mul_assign(omega_power);
PairingsBn254.Fr memory den = PairingsBn254.copy(at);
den.sub_assign(omega_power);
den.mul_assign(PairingsBn254.new_fr(domain_size));
den = den.inverse();
res.mul_assign(den);
}
function batch_evaluate_lagrange_poly_out_of_domain_old(
uint256[] memory poly_nums,
uint256 domain_size,
PairingsBn254.Fr memory omega,
PairingsBn254.Fr memory at
) internal view returns (PairingsBn254.Fr[] memory res) {
PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory tmp_1 = PairingsBn254.new_fr(0);
PairingsBn254.Fr memory tmp_2 = PairingsBn254.new_fr(domain_size);
PairingsBn254.Fr memory vanishing_at_z = at.pow(domain_size);
vanishing_at_z.sub_assign(one);
// we can not have random point z be in domain
require(vanishing_at_z.value != 0);
PairingsBn254.Fr[] memory nums = new PairingsBn254.Fr[](poly_nums.length);
PairingsBn254.Fr[] memory dens = new PairingsBn254.Fr[](poly_nums.length);
// numerators in a form omega^i * (z^n - 1)
// denoms in a form (z - omega^i) * N
for (uint256 i = 0; i < poly_nums.length; i++) {
tmp_1 = omega.pow(poly_nums[i]); // power of omega
nums[i].assign(vanishing_at_z);
nums[i].mul_assign(tmp_1);
dens[i].assign(at); // (X - omega^i) * N
dens[i].sub_assign(tmp_1);
dens[i].mul_assign(tmp_2); // mul by domain size
}
PairingsBn254.Fr[] memory partial_products = new PairingsBn254.Fr[](poly_nums.length);
partial_products[0].assign(PairingsBn254.new_fr(1));
for (uint256 i = 1; i < dens.length - 1; i++) {
partial_products[i].assign(dens[i - 1]);
partial_products[i].mul_assign(dens[i]);
}
tmp_2.assign(partial_products[partial_products.length - 1]);
tmp_2.mul_assign(dens[dens.length - 1]);
tmp_2 = tmp_2.inverse(); // tmp_2 contains a^-1 * b^-1 (with! the last one)
for (uint256 i = dens.length - 1; i < dens.length; i--) {
dens[i].assign(tmp_2); // all inversed
dens[i].mul_assign(partial_products[i]); // clear lowest terms
tmp_2.mul_assign(dens[i]);
}
for (uint256 i = 0; i < nums.length; i++) {
nums[i].mul_assign(dens[i]);
}
return nums;
}
function evaluate_vanishing_old(uint256 domain_size, PairingsBn254.Fr memory at)
internal
view
returns (PairingsBn254.Fr memory res)
{
res = at.pow(domain_size);
res.sub_assign(PairingsBn254.new_fr(1));
}
function verify_at_z(
PartialVerifierStateOld memory state,
ProofOld memory proof,
VerificationKeyOld memory vk
) internal view returns (bool) {
PairingsBn254.Fr memory lhs = evaluate_vanishing_old(vk.domain_size, state.z);
require(lhs.value != 0); // we can not check a polynomial relationship if point `z` is in the domain
lhs.mul_assign(proof.quotient_polynomial_at_z);
PairingsBn254.Fr memory quotient_challenge = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory rhs = PairingsBn254.copy(proof.linearization_polynomial_at_z);
// public inputs
PairingsBn254.Fr memory tmp = PairingsBn254.new_fr(0);
for (uint256 i = 0; i < proof.input_values.length; i++) {
tmp.assign(state.cached_lagrange_evals[i]);
tmp.mul_assign(PairingsBn254.new_fr(proof.input_values[i]));
rhs.add_assign(tmp);
}
quotient_challenge.mul_assign(state.alpha);
PairingsBn254.Fr memory z_part = PairingsBn254.copy(proof.grand_product_at_z_omega);
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
tmp.assign(proof.permutation_polynomials_at_z[i]);
tmp.mul_assign(state.beta);
tmp.add_assign(state.gamma);
tmp.add_assign(proof.wire_values_at_z[i]);
z_part.mul_assign(tmp);
}
tmp.assign(state.gamma);
// we need a wire value of the last polynomial in enumeration
tmp.add_assign(proof.wire_values_at_z[STATE_WIDTH_OLD - 1]);
z_part.mul_assign(tmp);
z_part.mul_assign(quotient_challenge);
rhs.sub_assign(z_part);
quotient_challenge.mul_assign(state.alpha);
tmp.assign(state.cached_lagrange_evals[0]);
tmp.mul_assign(quotient_challenge);
rhs.sub_assign(tmp);
return lhs.value == rhs.value;
}
function reconstruct_d(
PartialVerifierStateOld memory state,
ProofOld memory proof,
VerificationKeyOld memory vk
) internal view returns (PairingsBn254.G1Point memory res) {
// we compute what power of v is used as a delinearization factor in batch opening of
// commitments. Let's label W(x) = 1 / (x - z) *
// [
// t_0(x) + z^n * t_1(x) + z^2n * t_2(x) + z^3n * t_3(x) - t(z)
// + v (r(x) - r(z))
// + v^{2..5} * (witness(x) - witness(z))
// + v^(6..8) * (permutation(x) - permutation(z))
// ]
// W'(x) = 1 / (x - z*omega) *
// [
// + v^9 (z(x) - z(z*omega)) <- we need this power
// + v^10 * (d(x) - d(z*omega))
// ]
//
// we pay a little for a few arithmetic operations to not introduce another constant
uint256 power_for_z_omega_opening = 1 + 1 + STATE_WIDTH_OLD + STATE_WIDTH_OLD - 1;
res = PairingsBn254.copy_g1(vk.selector_commitments[STATE_WIDTH_OLD + 1]);
PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();
PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(0);
// addition gates
for (uint256 i = 0; i < STATE_WIDTH_OLD; i++) {
tmp_g1 = vk.selector_commitments[i].point_mul(proof.wire_values_at_z[i]);
res.point_add_assign(tmp_g1);
}
// multiplication gate
tmp_fr.assign(proof.wire_values_at_z[0]);
tmp_fr.mul_assign(proof.wire_values_at_z[1]);
tmp_g1 = vk.selector_commitments[STATE_WIDTH_OLD].point_mul(tmp_fr);
res.point_add_assign(tmp_g1);
// d_next
tmp_g1 = vk.next_step_selector_commitments[0].point_mul(proof.wire_values_at_z_omega[0]);
res.point_add_assign(tmp_g1);
// z * non_res * beta + gamma + a
PairingsBn254.Fr memory grand_product_part_at_z = PairingsBn254.copy(state.z);
grand_product_part_at_z.mul_assign(state.beta);
grand_product_part_at_z.add_assign(proof.wire_values_at_z[0]);
grand_product_part_at_z.add_assign(state.gamma);
for (uint256 i = 0; i < vk.permutation_non_residues.length; i++) {
tmp_fr.assign(state.z);
tmp_fr.mul_assign(vk.permutation_non_residues[i]);
tmp_fr.mul_assign(state.beta);
tmp_fr.add_assign(state.gamma);
tmp_fr.add_assign(proof.wire_values_at_z[i + 1]);
grand_product_part_at_z.mul_assign(tmp_fr);
}
grand_product_part_at_z.mul_assign(state.alpha);
tmp_fr.assign(state.cached_lagrange_evals[0]);
tmp_fr.mul_assign(state.alpha);
tmp_fr.mul_assign(state.alpha);
grand_product_part_at_z.add_assign(tmp_fr);
PairingsBn254.Fr memory grand_product_part_at_z_omega = state.v.pow(power_for_z_omega_opening);
grand_product_part_at_z_omega.mul_assign(state.u);
PairingsBn254.Fr memory last_permutation_part_at_z = PairingsBn254.new_fr(1);
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
tmp_fr.assign(state.beta);
tmp_fr.mul_assign(proof.permutation_polynomials_at_z[i]);
tmp_fr.add_assign(state.gamma);
tmp_fr.add_assign(proof.wire_values_at_z[i]);
last_permutation_part_at_z.mul_assign(tmp_fr);
}
last_permutation_part_at_z.mul_assign(state.beta);
last_permutation_part_at_z.mul_assign(proof.grand_product_at_z_omega);
last_permutation_part_at_z.mul_assign(state.alpha);
// add to the linearization
tmp_g1 = proof.grand_product_commitment.point_mul(grand_product_part_at_z);
tmp_g1.point_sub_assign(vk.permutation_commitments[STATE_WIDTH_OLD - 1].point_mul(last_permutation_part_at_z));
res.point_add_assign(tmp_g1);
res.point_mul_assign(state.v);
res.point_add_assign(proof.grand_product_commitment.point_mul(grand_product_part_at_z_omega));
}
function verify_commitments(
PartialVerifierStateOld memory state,
ProofOld memory proof,
VerificationKeyOld memory vk
) internal view returns (bool) {
PairingsBn254.G1Point memory d = reconstruct_d(state, proof, vk);
PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size);
PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();
PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1);
PairingsBn254.G1Point memory commitment_aggregation = PairingsBn254.copy_g1(proof.quotient_poly_commitments[0]);
PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(1);
for (uint256 i = 1; i < proof.quotient_poly_commitments.length; i++) {
tmp_fr.mul_assign(z_in_domain_size);
tmp_g1 = proof.quotient_poly_commitments[i].point_mul(tmp_fr);
commitment_aggregation.point_add_assign(tmp_g1);
}
aggregation_challenge.mul_assign(state.v);
commitment_aggregation.point_add_assign(d);
for (uint256 i = 0; i < proof.wire_commitments.length; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_g1 = proof.wire_commitments[i].point_mul(aggregation_challenge);
commitment_aggregation.point_add_assign(tmp_g1);
}
for (uint256 i = 0; i < vk.permutation_commitments.length - 1; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_g1 = vk.permutation_commitments[i].point_mul(aggregation_challenge);
commitment_aggregation.point_add_assign(tmp_g1);
}
aggregation_challenge.mul_assign(state.v);
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(aggregation_challenge);
tmp_fr.mul_assign(state.u);
tmp_g1 = proof.wire_commitments[STATE_WIDTH_OLD - 1].point_mul(tmp_fr);
commitment_aggregation.point_add_assign(tmp_g1);
// collect opening values
aggregation_challenge = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory aggregated_value = PairingsBn254.copy(proof.quotient_polynomial_at_z);
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.linearization_polynomial_at_z);
tmp_fr.mul_assign(aggregation_challenge);
aggregated_value.add_assign(tmp_fr);
for (uint256 i = 0; i < proof.wire_values_at_z.length; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.wire_values_at_z[i]);
tmp_fr.mul_assign(aggregation_challenge);
aggregated_value.add_assign(tmp_fr);
}
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.permutation_polynomials_at_z[i]);
tmp_fr.mul_assign(aggregation_challenge);
aggregated_value.add_assign(tmp_fr);
}
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.grand_product_at_z_omega);
tmp_fr.mul_assign(aggregation_challenge);
tmp_fr.mul_assign(state.u);
aggregated_value.add_assign(tmp_fr);
aggregation_challenge.mul_assign(state.v);
tmp_fr.assign(proof.wire_values_at_z_omega[0]);
tmp_fr.mul_assign(aggregation_challenge);
tmp_fr.mul_assign(state.u);
aggregated_value.add_assign(tmp_fr);
commitment_aggregation.point_sub_assign(PairingsBn254.P1().point_mul(aggregated_value));
PairingsBn254.G1Point memory pair_with_generator = commitment_aggregation;
pair_with_generator.point_add_assign(proof.opening_at_z_proof.point_mul(state.z));
tmp_fr.assign(state.z);
tmp_fr.mul_assign(vk.omega);
tmp_fr.mul_assign(state.u);
pair_with_generator.point_add_assign(proof.opening_at_z_omega_proof.point_mul(tmp_fr));
PairingsBn254.G1Point memory pair_with_x = proof.opening_at_z_omega_proof.point_mul(state.u);
pair_with_x.point_add_assign(proof.opening_at_z_proof);
pair_with_x.negate();
return PairingsBn254.pairingProd2(pair_with_generator, PairingsBn254.P2(), pair_with_x, vk.g2_x);
}
function verify_initial(
PartialVerifierStateOld memory state,
ProofOld memory proof,
VerificationKeyOld memory vk
) internal view returns (bool) {
require(proof.input_values.length == vk.num_inputs);
require(vk.num_inputs >= 1);
TranscriptLibrary.Transcript memory transcript = TranscriptLibrary.new_transcript();
for (uint256 i = 0; i < vk.num_inputs; i++) {
transcript.update_with_u256(proof.input_values[i]);
}
for (uint256 i = 0; i < proof.wire_commitments.length; i++) {
transcript.update_with_g1(proof.wire_commitments[i]);
}
state.beta = transcript.get_challenge();
state.gamma = transcript.get_challenge();
transcript.update_with_g1(proof.grand_product_commitment);
state.alpha = transcript.get_challenge();
for (uint256 i = 0; i < proof.quotient_poly_commitments.length; i++) {
transcript.update_with_g1(proof.quotient_poly_commitments[i]);
}
state.z = transcript.get_challenge();
uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs);
for (uint256 i = 0; i < lagrange_poly_numbers.length; i++) {
lagrange_poly_numbers[i] = i;
}
state.cached_lagrange_evals = batch_evaluate_lagrange_poly_out_of_domain_old(
lagrange_poly_numbers,
vk.domain_size,
vk.omega,
state.z
);
bool valid = verify_at_z(state, proof, vk);
if (valid == false) {
return false;
}
for (uint256 i = 0; i < proof.wire_values_at_z.length; i++) {
transcript.update_with_fr(proof.wire_values_at_z[i]);
}
for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
transcript.update_with_fr(proof.wire_values_at_z_omega[i]);
}
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
transcript.update_with_fr(proof.permutation_polynomials_at_z[i]);
}
transcript.update_with_fr(proof.quotient_polynomial_at_z);
transcript.update_with_fr(proof.linearization_polynomial_at_z);
transcript.update_with_fr(proof.grand_product_at_z_omega);
state.v = transcript.get_challenge();
transcript.update_with_g1(proof.opening_at_z_proof);
transcript.update_with_g1(proof.opening_at_z_omega_proof);
state.u = transcript.get_challenge();
return true;
}
// This verifier is for a PLONK with a state width 4
// and main gate equation
// q_a(X) * a(X) +
// q_b(X) * b(X) +
// q_c(X) * c(X) +
// q_d(X) * d(X) +
// q_m(X) * a(X) * b(X) +
// q_constants(X)+
// q_d_next(X) * d(X*omega)
// where q_{}(X) are selectors a, b, c, d - state (witness) polynomials
// q_d_next(X) "peeks" into the next row of the trace, so it takes
// the same d(X) polynomial, but shifted
function verify_old(ProofOld memory proof, VerificationKeyOld memory vk) internal view returns (bool) {
PartialVerifierStateOld memory state;
bool valid = verify_initial(state, proof, vk);
if (valid == false) {
return false;
}
valid = verify_commitments(state, proof, vk);
return valid;
}
}
contract VerifierWithDeserializeOld is Plonk4VerifierWithAccessToDNextOld {
uint256 constant SERIALIZED_PROOF_LENGTH_OLD = 33;
function deserialize_proof_old(uint256[] memory public_inputs, uint256[] memory serialized_proof)
internal
pure
returns (ProofOld memory proof)
{
require(serialized_proof.length == SERIALIZED_PROOF_LENGTH_OLD);
proof.input_values = new uint256[](public_inputs.length);
for (uint256 i = 0; i < public_inputs.length; i++) {
proof.input_values[i] = public_inputs[i];
}
uint256 j = 0;
for (uint256 i = 0; i < STATE_WIDTH_OLD; i++) {
proof.wire_commitments[i] = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
j += 2;
}
proof.grand_product_commitment = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
j += 2;
for (uint256 i = 0; i < STATE_WIDTH_OLD; i++) {
proof.quotient_poly_commitments[i] = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j + 1]
);
j += 2;
}
for (uint256 i = 0; i < STATE_WIDTH_OLD; i++) {
proof.wire_values_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
}
for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
proof.wire_values_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
}
proof.grand_product_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
proof.quotient_polynomial_at_z = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
proof.linearization_polynomial_at_z = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
proof.permutation_polynomials_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
j += 1;
}
proof.opening_at_z_proof = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
j += 2;
proof.opening_at_z_omega_proof = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
}
}pragma solidity ^0.7.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
/// @param newTargetInitializationParameters New target initialization parameters
function upgradeTarget(address newTarget, bytes calldata newTargetInitializationParameters) external;
}pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/// @title Ownable Contract
/// @author Matter Labs
contract Ownable {
/// @dev Storage position of the masters address (keccak256('eip1967.proxy.admin') - 1)
bytes32 private constant MASTER_POSITION = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/// @notice Contract constructor
/// @dev Sets msg sender address as masters address
/// @param masterAddress Master address
constructor(address masterAddress) {
setMaster(masterAddress);
}
/// @notice Check if specified address is master
/// @param _address Address to check
function requireMaster(address _address) internal view {
require(_address == getMaster(), "1c"); // oro11 - only by master
}
/// @notice Returns contract masters address
/// @return master Master's address
function getMaster() public view returns (address master) {
bytes32 position = MASTER_POSITION;
assembly {
master := sload(position)
}
}
/// @dev Sets new masters address
/// @param _newMaster New master's address
function setMaster(address _newMaster) internal {
bytes32 position = MASTER_POSITION;
assembly {
sstore(position, _newMaster)
}
}
/// @notice Transfer mastership of the contract to new master
/// @param _newMaster New masters address
function transferMastership(address _newMaster) external {
requireMaster(msg.sender);
require(_newMaster != address(0), "1d"); // otp11 - new masters address can't be zero address
setMaster(_newMaster);
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"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":"BlockVerification","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":[{"indexed":true,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"zkSyncBlockId","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"accountId","type":"uint32"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"DepositCommit","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":"uint32","name":"zkSyncBlockId","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"accountId","type":"uint32"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"FullExitCommit","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":false,"internalType":"uint256","name":"newNoticePeriod","type":"uint256"}],"name":"NoticePeriodChange","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":"uint32","name":"tokenId","type":"uint32"}],"name":"WithdrawalNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"tokenId","type":"uint32"}],"name":"WithdrawalNFTPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"WithdrawalPending","type":"event"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint128","name":"_amount","type":"uint128"},{"internalType":"uint128","name":"_maxAmount","type":"uint128"}],"name":"_transferERC20","outputs":[{"internalType":"uint128","name":"withdrawnAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"activateExodusMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","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":"uint64","name":"_n","type":"uint64"},{"internalType":"bytes[]","name":"_depositsPubdata","type":"bytes[]"}],"name":"cancelOutstandingDepositsForExodusMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint64","name":"priorityOperations","type":"uint64"},{"internalType":"bytes32","name":"pendingOnchainOperationsHash","type":"bytes32"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"stateHash","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBlockInfo","name":"_lastCommittedBlockData","type":"tuple"},{"components":[{"internalType":"bytes32","name":"newStateHash","type":"bytes32"},{"internalType":"bytes","name":"publicData","type":"bytes"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"components":[{"internalType":"bytes","name":"ethWitness","type":"bytes"},{"internalType":"uint32","name":"publicDataOffset","type":"uint32"}],"internalType":"struct ZkSync.OnchainOperationData[]","name":"onchainOperations","type":"tuple[]"},{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint32","name":"feeAccount","type":"uint32"}],"internalType":"struct ZkSync.CommitBlockInfo[]","name":"_newBlocksData","type":"tuple[]"}],"name":"commitBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cutUpgradeNoticePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint104","name":"_amount","type":"uint104"},{"internalType":"address","name":"_zkSyncAddress","type":"address"}],"name":"depositERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_zkSyncAddress","type":"address"}],"name":"depositETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint64","name":"priorityOperations","type":"uint64"},{"internalType":"bytes32","name":"pendingOnchainOperationsHash","type":"bytes32"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"stateHash","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBlockInfo","name":"storedBlock","type":"tuple"},{"internalType":"bytes[]","name":"pendingOnchainOpsPubdata","type":"bytes[]"}],"internalType":"struct ZkSync.ExecuteBlockInfo[]","name":"_blocksData","type":"tuple[]"}],"name":"executeBlocks","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":"getNoticePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"address","name":"_token","type":"address"}],"name":"getPendingBalance","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","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":[{"components":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint64","name":"priorityOperations","type":"uint64"},{"internalType":"bytes32","name":"pendingOnchainOperationsHash","type":"bytes32"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"stateHash","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBlockInfo","name":"_storedBlockInfo","type":"tuple"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint32","name":"_accountId","type":"uint32"},{"internalType":"uint32","name":"_tokenId","type":"uint32"},{"internalType":"uint128","name":"_amount","type":"uint128"},{"internalType":"uint32","name":"_nftCreatorAccountId","type":"uint32"},{"internalType":"address","name":"_nftCreatorAddress","type":"address"},{"internalType":"uint32","name":"_nftSerialId","type":"uint32"},{"internalType":"bytes32","name":"_nftContentHash","type":"bytes32"},{"internalType":"uint256[]","name":"_proof","type":"uint256[]"}],"name":"performExodus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint64","name":"priorityOperations","type":"uint64"},{"internalType":"bytes32","name":"pendingOnchainOperationsHash","type":"bytes32"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"stateHash","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBlockInfo[]","name":"_committedBlocks","type":"tuple[]"},{"components":[{"internalType":"uint256[]","name":"recursiveInput","type":"uint256[]"},{"internalType":"uint256[]","name":"proof","type":"uint256[]"},{"internalType":"uint256[]","name":"commitments","type":"uint256[]"},{"internalType":"uint8[]","name":"vkIndexes","type":"uint8[]"},{"internalType":"uint256[16]","name":"subproofsLimbs","type":"uint256[16]"}],"internalType":"struct ZkSync.ProofInput","name":"_proof","type":"tuple"}],"name":"proveBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_accountId","type":"uint32"},{"internalType":"address","name":"_token","type":"address"}],"name":"requestFullExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_accountId","type":"uint32"},{"internalType":"uint32","name":"_tokenId","type":"uint32"}],"name":"requestFullExitNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint64","name":"priorityOperations","type":"uint64"},{"internalType":"bytes32","name":"pendingOnchainOperationsHash","type":"bytes32"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"stateHash","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBlockInfo[]","name":"_blocksToRevert","type":"tuple[]"}],"name":"revertBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_pubkeyHash","type":"bytes"},{"internalType":"uint32","name":"_nonce","type":"uint32"}],"name":"setAuthPubkeyHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"storedBlockHashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":"totalOpenPriorityRequests","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"upgradeParameters","type":"bytes"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upgradeCanceled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upgradeFinishes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upgradeNoticePeriodStarted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upgradePreparationStarted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_owner","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint128","name":"_amount","type":"uint128"}],"name":"withdrawPendingBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_tokenId","type":"uint32"}],"name":"withdrawPendingNFTBalance","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b5061598580620000216000396000f3fe6080604052600436106101ee5760003560e01c80637efcfe851161010d578063ab9b2adf116100a0578063c57b22be1161006f578063c57b22be14610524578063d514da5014610539578063e17376b514610559578063f223548714610579578063faf4d8cb1461058e576101ee565b8063ab9b2adf146104c4578063b0705b42146104e4578063b269b9ae14610425578063b4a8498c14610504576101ee565b80638ae20dc9116100dc5780638ae20dc91461044f5780638ee1a74e1461046f5780639ba0d1461461048f578063a7e7aacd146104af576101ee565b80637efcfe85146103e55780638398180814610405578063871b8ff1146104255780638773334c1461043a576101ee565b8063439fab91116101855780635aca41f6116101545780635aca41f61461035f578063647b59231461038c57806367708dae146103ae57806378b91e70146103d0576101ee565b8063439fab91146102df57806345269298146102ff578063505a75731461031f578063595a5ebc1461033f576101ee565b80632a3174f4116101c15780632a3174f4146102805780632d2da806146102a25780633b154b73146102b55780633e71e1e7146102ca576101ee565b806313d9787b146101f35780631d179643146102155780632539464514610235578063264c091214610255575b600080fd5b3480156101ff57600080fd5b5061021361020e366004614dca565b6105a3565b005b34801561022157600080fd5b50610213610230366004614b52565b6106e9565b34801561024157600080fd5b50610213610250366004614a1d565b6106fd565b34801561026157600080fd5b5061026a610731565b604051610277919061531e565b60405180910390f35b34801561028c57600080fd5b5061029561073a565b6040516102779190615329565b6102136102b0366004614691565b610740565b3480156102c157600080fd5b50610213610788565b3480156102d657600080fd5b5061021361078e565b3480156102eb57600080fd5b506102136102fa366004614a1d565b610798565b34801561030b57600080fd5b5061021361031a366004614c29565b6108c8565b34801561032b57600080fd5b5061021361033a366004614d95565b610aef565b34801561034b57600080fd5b5061021361035a366004614a5c565b610d6f565b34801561036b57600080fd5b5061037f61037a366004614763565b610d7c565b6040516102779190615832565b34801561039857600080fd5b506103a1610e4d565b6040516102779190615855565b3480156103ba57600080fd5b506103c3610e59565b6040516102779190615885565b3480156103dc57600080fd5b50610213610e68565b3480156103f157600080fd5b50610213610400366004614de5565b610e96565b34801561041157600080fd5b506102136104203660046148d9565b610ea2565b34801561043157600080fd5b506102136110d3565b34801561044657600080fd5b5061026a6110db565b34801561045b57600080fd5b5061029561046a36600461479b565b6110e5565b34801561047b57600080fd5b5061037f61048a366004614aad565b611102565b34801561049b57600080fd5b506102956104aa366004614d95565b6112a1565b3480156104bb57600080fd5b5061026a6112b3565b3480156104d057600080fd5b506102136104df366004614daf565b61136b565b3480156104f057600080fd5b506102136104ff3660046147c6565b611540565b34801561051057600080fd5b5061021361051f3660046148a7565b611775565b34801561053057600080fd5b506103c361177d565b34801561054557600080fd5b50610213610554366004614719565b611793565b34801561056557600080fd5b50610213610574366004614b08565b6119c2565b34801561058557600080fd5b506103a1611cd6565b34801561059a57600080fd5b506103a1611ce9565b60008051602061593083398151915254600181146105c057600080fd5b6002600080516020615930833981519152556105da611cfc565b62ffffff63ffffffff8416111561060c5760405162461bcd60e51b81526004016106039061567f565b60405180910390fd5b63ffffffff831662ffffff14156106355760405162461bcd60e51b815260040161060390615649565b63ffffffff821661ffff1080156106555750637ffffffe63ffffffff8316105b6106715760405162461bcd60e51b815260040161060390615589565b604080516101008101825263ffffffff80861682523360208301528416918101919091526000606082018190526080820181905260a0820181905260c0820181905260e082018190526106c382611d1f565b90506106d0600682611d60565b5050600160008051602061593083398151915255505050565b6106f1611ec8565b50505050505050505050565b600080516020615930833981519152546001811461071a57600080fd5b505060016000805160206159308339815191525550565b60095460ff1681565b60005b90565b6001600160a01b0381811614156107695760405162461bcd60e51b815260040161060390615553565b610771611cfc565b610785600061077f34611f03565b83611f4a565b50565b42601555565b610796611ec8565b565b6107a0611fdd565b60008080806107b1858701876146c9565b600280546001600160a01b038086166001600160a01b0319928316179092556003805483881690831617905560138054928516929091169190911790556040805160c0810182526000808252602082018190527fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47092820192909252606081018290526080810183905260a0810191909152939750919550935091506108558161202b565b60008052600d6020527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee556212750060148190556040517ff2b18f8abbd8a0d0c1fb8245146eedf5304887b12f6395b548ca238e054a1483916108b791615329565b60405180910390a150505050505050565b60008051602061593083398151915254600181146108e557600080fd5b6002600080516020615930833981519152556108ff611cfc565b600354604051634b18bd0f60e01b81526001600160a01b0390911690634b18bd0f9061092f9033906004016151b4565b60006040518083038186803b15801561094757600080fd5b505afa15801561095b573d6000803e3d6000fd5b505050506109688361202b565b600654600160601b900463ffffffff166000908152600d6020526040902054146109a45760405162461bcd60e51b81526004016106039061578e565b60005b82518163ffffffff161015610a6e576109d984848363ffffffff16815181106109cc57fe5b602002602001015161205b565b6020810151600c80546001600160401b03600160801b80830482169094011690920267ffffffffffffffff60801b199092169190911790559350610a1c8461202b565b845163ffffffff9081166000908152600d6020526040808220939093558651925192909116917f81a92942d0f9c33b897a438384c9c3d88be397776138efa3ba1a4fc8b62684249190a26001016109a7565b5081516006805463ffffffff600160601b80830482169094011690920263ffffffff60601b19909216919091179055600c546001600160401b03600160401b82048116600160801b909204161115610ad85760405162461bcd60e51b81526004016106039061569a565b600160008051602061593083398151915255505050565b6000805160206159308339815191525460018114610b0c57600080fd5b600260008051602061593083398151915281905563ffffffff808416600090815260126020908152604091829020825160c081018452815480861682526001600160a01b0364010000000082048116948301859052600160c01b909104861694820194909452600182015460608201529401549182166080850152600160a01b90910490911660a0830152610bb35760405162461bcd60e51b8152600401610603906156d0565b6003548151602083015160405163b79eb8c760e01b81526000936001600160a01b03169263b79eb8c792610be992600401615866565b60206040518083038186803b158015610c0157600080fd5b505afa158015610c15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3991906146ad565b9050806001600160a01b031663234ce590836020015184608001518560000151866040015187606001518860a001516040518763ffffffff1660e01b8152600401610c8996959493929190615234565b600060405180830381600087803b158015610ca357600080fd5b505af1158015610cb7573d6000803e3d6000fd5b5050505060a08201805163ffffffff90811660009081526011602052604080822080546001600160a01b0319166001600160a01b0387161790559251925192909116917f0b9f3586023bf754b8d962232407f7ac4d90fd19a1c4756c6619927abf0675609190a250505063ffffffff16600090815260126020526040812080546001600160e01b031916815560018082019290925560020180546001600160c01b031916905560008051602061593083398151915255565b610d77611ec8565b505050565b6000806001600160a01b03831615610e11576003546040516375698bb160e11b81526001600160a01b039091169063ead3176290610dbe9086906004016151b4565b60206040518083038186803b158015610dd657600080fd5b505afa158015610dea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0e9190614d73565b90505b60046000610e1f8684612189565b6001600160501b03191681526020810191909152604001600020546001600160801b03169150505b92915050565b600e5463ffffffff1681565b600c546001600160401b031681565b6000805460ff19166001908117909155429055601454601554610e8a916121a6565b42101561079657600080fd5b610e9e611ec8565b5050565b6000805160206159308339815191525460018114610ebf57600080fd5b600260008051602061593083398151915255610ed9611cfc565b600e5463ffffffff1660005b8451811015610fb35763ffffffff60018301166000908152600d60205260409020548551610f2690879084908110610f1957fe5b602002602001015161202b565b14610f435760405162461bcd60e51b8152600401610603906155a4565b8160010191506001600160fd1b03858281518110610f5d57fe5b602002602001015160a0015160001c166001600160fd1b0385604001518381518110610f8557fe5b60200260200101511614610fab5760405162461bcd60e51b8152600401610603906154c6565b600101610ee5565b506002548351602085015160608601516040808801516080890151915163054185eb60e51b81526000966001600160a01b03169563a830bd6095610fff95919490939192600401615272565b60206040518083038186803b15801561101757600080fd5b505afa15801561102b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104f91906149e5565b90508061106e5760405162461bcd60e51b815260040161060390615408565b60065463ffffffff600160601b909104811690831611156110a15760405162461bcd60e51b81526004016106039061545a565b50600e805463ffffffff191663ffffffff92909216919091179055505060016000805160206159308339815191525550565b6107966121ec565b60095460ff161590565b600a60209081526000928352604080842090915290825290205481565b60003330146111235760405162461bcd60e51b815260040161060390615707565b6040516370a0823160e01b81526000906001600160a01b038716906370a08231906111529030906004016151b4565b60206040518083038186803b15801561116a57600080fd5b505afa15801561117e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a29190614a05565b90506111b88686866001600160801b031661226f565b6111d45760405162461bcd60e51b815260040161060390615758565b6040516370a0823160e01b81526000906001600160a01b038816906370a08231906112039030906004016151b4565b60206040518083038186803b15801561121b57600080fd5b505afa15801561122f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112539190614a05565b905060006112618383612395565b9050846001600160801b031681111561128c5760405162461bcd60e51b8152600401610603906154ab565b61129581611f03565b98975050505050505050565b600d6020526000908152604090205481565b600c546001600160401b039081166000908152600f602052604081205490918291600160a01b900416431080159061130e5750600c546001600160401b039081166000908152600f6020526040902054600160a01b90041615155b905080156113615760095460ff16611357576009805460ff191660011790556040517fc71028c67eb0ef128ea270a59a674629e767d51c1af44ed6753fd2fad2c7b67790600090a15b600191505061073d565b600091505061073d565b600080516020615930833981519152546001811461138857600080fd5b6002600080516020615930833981519152556113a2611cfc565b62ffffff63ffffffff841611156113cb5760405162461bcd60e51b81526004016106039061567f565b63ffffffff831662ffffff14156113f45760405162461bcd60e51b815260040161060390615649565b60006001600160a01b03831661140c5750600061148f565b6003546040516375698bb160e11b81526001600160a01b039091169063ead317629061143c9086906004016151b4565b60206040518083038186803b15801561145457600080fd5b505afa158015611468573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148c9190614d73565b90505b604080516101008101825263ffffffff8616815233602082015261ffff8316918101919091526000606082018190526080820181905260a0820181905260c0820181905260e082018190526114e382611d1f565b90506114f0600682611d60565b60006114fc3385612189565b6001600160501b0319166000908152600460205260409020805460ff60801b191660ff60801b17905550506001600080516020615930833981519152555050505050565b600080516020615930833981519152546001811461155d57600080fd5b600260008051602061593083398151915255611577611cfc565b600354604051634b18bd0f60e01b81526001600160a01b0390911690634b18bd0f906115a79033906004016151b4565b60006040518083038186803b1580156115bf57600080fd5b505afa1580156115d3573d6000803e3d6000fd5b50508351600092509050815b8163ffffffff168163ffffffff16101561169857611616858263ffffffff168151811061160857fe5b6020026020010151826123bb565b848163ffffffff168151811061162857fe5b6020026020010151600001516020015183019250848163ffffffff168151811061164e57fe5b6020026020010151600001516000015163ffffffff167f0cdbd8bd7813095001c5fe7917bd69d834dc01db7c1dfcf52ca135bd2038441360405160405180910390a26001016115df565b50600c805467ffffffffffffffff60401b1967ffffffffffffffff60801b1967ffffffffffffffff1983166001600160401b039384168701841617908116600160801b918290048416879003841690910217908116600160401b918290048316869003909216810291909117909155600680546bffffffff00000000000000001981169083900463ffffffff9081168501811684029190911791829055600e5481169290910416111561175d5760405162461bcd60e51b815260040161060390615490565b50506001600080516020615930833981519152555050565b610785611ec8565b600c54600160401b90046001600160401b031681565b60008051602061593083398151915254600181146117b057600080fd5b6002600080516020615930833981519152556001600160a01b038316611867576117dc600083866126e0565b6000846001600160a01b0316836001600160801b03166040516117fe9061073d565b60006040518083038185875af1925050503d806000811461183b576040519150601f19603f3d011682016040523d82523d6000602084013e611840565b606091505b50509050806118615760405162461bcd60e51b8152600401610603906157a9565b506119aa565b6003546040516375698bb160e11b81526000916001600160a01b03169063ead31762906118989087906004016151b4565b60206040518083038186803b1580156118b057600080fd5b505afa1580156118c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e89190614d73565b905060006118f68683612189565b6001600160501b031981166000908152600460208190526040808320549051634770d3a760e11b81529394506001600160801b0316923091638ee1a74e91611946918b918d918c91899101615332565b602060405180830381600087803b15801561196057600080fd5b505af1158015611974573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119989190614d57565b90506119a584828a6126e0565b505050505b60016000805160206159308339815191525550505050565b60008051602061593083398151915254600181146119df57600080fd5b6002600080516020615930833981519152556001600160a01b038281161415611a1a5760405162461bcd60e51b815260040161060390615553565b611a22611cfc565b6003546040516375698bb160e11b81526000916001600160a01b03169063ead3176290611a539088906004016151b4565b60206040518083038186803b158015611a6b57600080fd5b505afa158015611a7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aa39190614d73565b60035460405163f3a65bf960e01b81529192506001600160a01b03169063f3a65bf990611ad4908490600401615846565b60206040518083038186803b158015611aec57600080fd5b505afa158015611b00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b2491906149e5565b15611b415760405162461bcd60e51b8152600401610603906156b5565b6040516370a0823160e01b81526000906001600160a01b038716906370a0823190611b709030906004016151b4565b60206040518083038186803b158015611b8857600080fd5b505afa158015611b9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc09190614a05565b9050611be8863330611bda896001600160681b0316611f03565b6001600160801b031661278a565b611c045760405162461bcd60e51b8152600401610603906153b7565b6040516370a0823160e01b81526000906001600160a01b038816906370a0823190611c339030906004016151b4565b60206040518083038186803b158015611c4b57600080fd5b505afa158015611c5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c839190614a05565b90506000611c99611c948385612395565b611f03565b90506001600160681b036001600160801b0382161115611ccb5760405162461bcd60e51b815260040161060390615366565b6119a5848288611f4a565b600654600160401b900463ffffffff1681565b600654600160601b900463ffffffff1681565b60095460ff16156107965760405162461bcd60e51b8152600401610603906155c0565b6060600682516020808501516040808701519051611d4a95949360009182918291829182910161511f565b6040516020818303038152906040529050919050565b600c544361438001906001600160401b03808216600160401b90920416016000611d89846128b6565b90506040518060600160405280826bffffffffffffffffffffffff19168152602001846001600160401b0316815260200186600b811115611dc657fe5b90526001600160401b038084166000908152600f60209081526040918290208451815492860151909416600160a01b0267ffffffffffffffff60a01b1960609590951c6001600160a01b03199093169290921793909316178083559083015190829060ff60e01b1916600160e01b83600b811115611e4057fe5b02179055509050507fd0943372c08b438a88d4b39d77216901079eda9ca59d45349841c099083b683033838787876001600160401b0316604051611e889594939291906151c8565b60405180910390a15050600c805460016001600160401b03600160401b808404821692909201160267ffffffffffffffff60401b19909116179055505050565b6013546040516001600160a01b039091169036600082376000803683855af43d806000843e818015611ef8578184f35b8184fd5b5050505050565b6000600160801b8210611f42576040805162461bcd60e51b8152602060048201526002602482015261189b60f11b604482015290519081900360640190fd5b50805b919050565b60408051608081018252600080825261ffff861660208301526001600160801b038516928201929092526001600160a01b038316606082015290611f8d826128c4565b9050611f9a600182611d60565b8461ffff167f8f5f51448394699ad6a3b80cdadf4ec68c5d724c8c3fea09bea55b3c2d0e2dd085604051611fce9190615832565b60405180910390a25050505050565b600080516020615930833981519152805460019091558015610785576040805162461bcd60e51b815260206004820152600260248201526118a160f11b604482015290519081900360640190fd5b60008160405160200161203e91906157df565b604051602081830303815290604052805190602001209050919050565b6120636141c3565b826000015160010163ffffffff16826080015163ffffffff16146120995760405162461bcd60e51b815260040161060390615722565b8260600151826040015110156120c15760405162461bcd60e51b8152600401610603906153d2565b60408201516000906120d64262015180612395565b1115905060006120e8426103846121a6565b8460400151111590508180156120fb5750805b6121175760405162461bcd60e51b815260040161060390615664565b50506000806000612127856128eb565b925092509250600061213a878784612cd3565b6040805160c0810182526080808a015163ffffffff1682526001600160401b039096166020820152808201969096528701516060860152865193850193909352505060a0820152905092915050565b60a01b61ffff60a01b166001600160a01b03919091161760501b90565b6000828201838110156121e5576040805162461bcd60e51b81526020600482015260026024820152610c4d60f21b604482015290519081900360640190fd5b9392505050565b6000805460ff191681556001556212750060148190556040517ff2b18f8abbd8a0d0c1fb8245146eedf5304887b12f6395b548ca238e054a14839161223091615329565b60405180910390a1600060158190555b600f811015612267576000818152601660205260409020805460ff19169055600101612240565b506000601755565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17815292518251600094859485948a16939092909182918083835b602083106122ed5780518252601f1990920191602091820191016122ce565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461234f576040519150601f19603f3d011682016040523d82523d6000602084013e612354565b606091505b5091509150600081516000148061237e575081806020019051602081101561237b57600080fd5b50515b905082801561238a5750805b979650505050505050565b60006121e58383604051806040016040528060018152602001603b60f91b815250612f16565b81515163ffffffff166000908152600d602052604090205482516123de9061202b565b146123fb5760405162461bcd60e51b8152600401610603906154e1565b600654825151600160401b90910463ffffffff908116830160010181169116146124375760405162461bcd60e51b8152600401610603906157c4565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060005b8360200151518163ffffffff1610156126bb57600084602001518263ffffffff168151811061248657fe5b6020026020010151905060008160008151811061249f57fe5b016020015160f81c600b8111156124b257fe5b9050600381600b8111156124c257fe5b14156125245760006124d383612fad565b905061ffff63ffffffff16816000015163ffffffff1611156125075760405162461bcd60e51b8152600401610603906155db565b61251e816000015182604001518360200151613008565b506126a2565b600881600b81111561253257fe5b1415612577576000612543836131d4565b905061ffff63ffffffff16816000015163ffffffff1611156125075760405162461bcd60e51b815260040161060390615536565b600681600b81111561258557fe5b1415612660576000612596836131e8565b905061ffff63ffffffff16816040015163ffffffff16116125cd576125c8816040015182602001518360600151613008565b61251e565b80606001516001600160801b03166001141561251e5760006040518060c00160405280836080015163ffffffff1681526020018360a001516001600160a01b031681526020018360c0015163ffffffff1681526020018360e00151815260200183602001516001600160a01b03168152602001836040015163ffffffff168152509050612659816132d0565b50506126a2565b600a81600b81111561266e57fe5b141561268a57600061267f83613529565b905061251e816132d0565b60405162461bcd60e51b815260040161060390615500565b6126ac84836135c0565b9350505080600101905061245b565b508251604001518114610d775760405162461bcd60e51b81526004016106039061573d565b60006126ec8285612189565b6001600160501b031981166000908152600460205260409020549091506001600160801b031661271c81856135cf565b6001600160501b031983166000908152600460205260409081902080546001600160801b0319166001600160801b0393909316929092179091555161ffff8616907ff4bf32c167ee6e782944cd1db8174729b46adcd3bc732e282cc4a8079393315490611fce908790615832565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17815292518251600094859485948b16939092909182918083835b602083106128105780518252601f1990920191602091820191016127f1565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612872576040519150601f19603f3d011682016040523d82523d6000602084013e612877565b606091505b509150915060008151600014806128a1575081806020019051602081101561289e57600080fd5b50515b90508280156112955750979650505050505050565b805160209091012060601b90565b6060600160208084015160408086015160608701519151611d4a95946000949391016150c1565b6020810151600c5481517fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470926000926060926001600160401b03808316600160801b909304169190910190600a9006156129575760405162461bcd60e51b815260040161060390615381565b8151600a90046001600160401b038111801561297257600080fd5b506040519080825280601f01601f19166020018201604052801561299d576020820181803683370190505b50925060005b866060015151811015612cc9576000876060015182815181106129c257fe5b602002602001015190506000816020015163ffffffff169050845181106129fb5760405162461bcd60e51b81526004016106039061543e565b600a810615612a1c5760405162461bcd60e51b8152600401610603906153ed565b6000600a82049050868181518110612a3057fe5b01602001516001600160f81b03191615612a5c5760405162461bcd60e51b815260040161060390615366565b600160f81b878281518110612a6d57fe5b60200101906001600160f81b031916908160001a9053506000868381518110612a9257fe5b016020015160f81c600b811115612aa557fe5b9050600181600b811115612ab557fe5b1415612af1576000612ac98885603c6135f6565b90506000612ad6826136b2565b9050612ae4818c8a0161373b565b5050600190980197612cba565b600781600b811115612aff57fe5b1415612be4576000612b138885603c6135f6565b90506000612b20826137d1565b86515190915015612b60576000612b3b876000015183613841565b905080612b5a5760405162461bcd60e51b81526004016106039061551b565b50612bdd565b60008160200151604051602001612b779190614ec0565b60408051601f198184030181529181528151602092830120848201516001600160a01b03166000908152600a8452828120606087015163ffffffff16825290935291205414905080612bdb5760405162461bcd60e51b815260040161060390615475565b505b5050612cba565b6060600382600b811115612bf457fe5b1415612c0d57612c068885603c6135f6565b9050612cac565b600882600b811115612c1b57fe5b1415612c2d57612c068885603c6135f6565b600a82600b811115612c3b57fe5b1415612c4d57612c06888560646135f6565b600682600b811115612c5b57fe5b1415612c9457612c6d8885606e6135f6565b90506000612c7a826131e8565b9050612c88818c8a016138fe565b50600190990198612cac565b60405162461bcd60e51b815260040161060390615773565b612cb68b826135c0565b9a50505b505050508060010190506129a3565b5050509193909250565b6000806002846080015163ffffffff168560a0015163ffffffff16604051602001612cff929190614eed565b60408051601f1981840301815290829052612d1991614efb565b602060405180830381855afa158015612d36573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612d599190614a05565b90506002818660800151604051602001612d74929190614eed565b60408051601f1981840301815290829052612d8e91614efb565b602060405180830381855afa158015612dab573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612dce9190614a05565b8451604051919250600291612de7918491602001614eed565b60408051601f1981840301815290829052612e0191614efb565b602060405180830381855afa158015612e1e573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612e419190614a05565b90506002818560400151604051602001612e5c929190614eed565b60408051601f1981840301815290829052612e7691614efb565b602060405180830381855afa158015612e93573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612eb69190614a05565b90506000846020015184604051602001612ed1929190614f17565b60405160208183030381529060405290506040518151838352602082602083018560025afa818452808015612f0557612f07565bfe5b50509051979650505050505050565b60008184841115612fa55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612f6a578181015183820152602001612f52565b50505050905090810190601f168015612f975780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b612fb56141f8565b6005612fc1838261398e565b63ffffffff1683529050612fd583826139a7565b6001600160801b031660208401526002019050612ff283826139b7565b6001600160a01b03166040840152509092915050565b60006130148385612189565b9050600061ffff851661303d5783613035816001600160801b0386166139c7565b91505061314f565b6003546040516310603dad60e01b81526000916001600160a01b0316906310603dad9061306e908990600401615846565b60206040518083038186803b15801561308657600080fd5b505afa15801561309a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130be91906146ad565b604051634770d3a760e11b81529091503090638ee1a74e90620186a0906130ef9085908a908a908190600401615332565b602060405180830381600088803b15801561310957600080fd5b5087f19350505050801561313a575060408051601f3d908101601f1916820190925261313791810190614d57565b60015b613147576000915061314d565b50600191505b505b8015613196578461ffff167ff4bf32c167ee6e782944cd1db8174729b46adcd3bc732e282cc4a80793933154846040516131899190615832565b60405180910390a2611efc565b6131a08284613a33565b8461ffff167fd19cf67bbb6c320849f41b650b1179fb06a3f104451c75109c3b006a385c168884604051611fce9190615832565b6131dc6141f8565b6009612fc1838261398e565b6131f0614218565b60016131fc838261398e565b63ffffffff168352905061321083826139b7565b6001600160a01b03166020840152905061322a838261398e565b63ffffffff166040840152905061324183826139a7565b6001600160801b03166060840152905061325b838261398e565b63ffffffff166080840152905061327283826139b7565b6001600160a01b031660a0840152905061328c838261398e565b63ffffffff1660c084015290506132a38382613ad1565b60e08401529050606981146132ca5760405162461bcd60e51b8152600401610603906156ec565b50919050565b6003548151602083015160405163b79eb8c760e01b81526000936001600160a01b03169263b79eb8c79261330692600401615866565b60206040518083038186803b15801561331e57600080fd5b505afa158015613332573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335691906146ad565b9050806001600160a01b031663234ce590620493e0846020015185608001518660000151876040015188606001518960a001516040518863ffffffff1660e01b81526004016133aa96959493929190615234565b600060405180830381600088803b1580156133c457600080fd5b5087f1935050505080156133d6575060015b6134c25760a08201805163ffffffff90811660009081526012602090815260408083208751815493890151838a015163ffffffff1990951691871691909117640100000000600160c01b0319166401000000006001600160a01b03928316021763ffffffff60c01b1916600160c01b948716949094029390931781556060880151600182015560808801516002909101805496516001600160a01b0319909716919093161763ffffffff60a01b1916600160a01b9590941694850293909317905590517f1d19de6753fc463923c3d90b6fb45aca536732ed7c5ca377d4b3f63dfbe4d8199190a2610e9e565b60a08201805163ffffffff90811660009081526011602052604080822080546001600160a01b0319166001600160a01b0387161790559251925192909116917f0b9f3586023bf754b8d962232407f7ac4d90fd19a1c4756c6619927abf0675609190a25050565b6135316141c3565b600561353d838261398e565b63ffffffff168352905061355183826139b7565b6001600160a01b03166020840152905061356b838261398e565b63ffffffff16604084015290506135828382613ad1565b6060840152905061359383826139b7565b6001600160a01b0316608084015290506135ad838261398e565b63ffffffff1660a0840152509092915050565b80519181526020909101902090565b60006121e5838360405180604001604052806002815260200161616160f01b815250613ae1565b606081830184511015613634576040805162461bcd60e51b81526020600482015260016024820152602d60f91b604482015290519081900360640190fd5b6000826001600160401b038111801561364c57600080fd5b506040519080825280601f01601f191660200182016040528015613677576020820181803683370190505b50905082156136aa57602081018381016020860187015b818310156136a657805183526020928301920161368e565b5050505b949350505050565b6136ba61425c565b60016136c6838261398e565b63ffffffff16835290506136da838261398e565b63ffffffff16602084015290506136f183826139a7565b6001600160801b03166040840152905061370b83826139b7565b6001600160a01b031660608401529050602d81146132ca5760405162461bcd60e51b81526004016106039061556e565b6001600160401b0381166000908152600f6020526040902054600160e01b900460ff16600181600b81111561376c57fe5b146137895760405162461bcd60e51b815260040161060390615423565b6001600160401b0382166000908152600f602052604090205460601b6137af8482613b46565b6137cb5760405162461bcd60e51b8152600401610603906155f8565b50505050565b6137d961425c565b60016137e5838261398e565b63ffffffff16835290506137f98382613b76565b6001600160601b0319166020840152905061381483826139b7565b6001600160a01b03166040840152905061382e838261398e565b63ffffffff166060840152509092915050565b6000808360008151811061385157fe5b016020015160f81c600381111561386457fe5b9050600081600381111561387457fe5b141561388c576138848484613b86565b915050610e47565b600181600381111561389a57fe5b14156138aa576138848484613c1e565b60028160038111156138b857fe5b14156138c8576138848484613cf6565b60038160038111156138d657fe5b14156138e6576138848484613d6e565b60405162461bcd60e51b81526004016106039061539c565b6001600160401b0381166000908152600f6020526040902054600160e01b900460ff16600681600b81111561392f57fe5b1461394c5760405162461bcd60e51b815260040161060390615613565b6001600160401b0382166000908152600f602052604090205460601b6139728482613e14565b6137cb5760405162461bcd60e51b81526004016106039061562e565b60048101600061399e8484613e2d565b90509250929050565b60108101600061399e8484613e7b565b60148101600061399e8484613ebe565b600080836001600160a01b0316620186a0846040516139e59061073d565b600060405180830381858888f193505050503d8060008114613a23576040519150601f19603f3d011682016040523d82523d6000602084013e613a28565b606091505b509095945050505050565b6001600160501b03198216600090815260046020526040908190205481518083019092526001600160801b03169080613a6c8385613f01565b6001600160801b03908116825260ff60209283018190526001600160501b031990961660009081526004835260409020835181549490930151909616600160801b0260ff60801b19929091166001600160801b03199093169290921716179092555050565b60208101600061399e8484613f4c565b6000836001600160801b0316836001600160801b031611158290612fa55760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315612f6a578181015183820152602001612f52565b60006001600160601b03198216613b64613b5f856128c4565b6128b6565b6001600160601b031916149392505050565b60148101600061399e8484613f8f565b600080613b968460016041613fd7565b91505060008360200151846060015185600001516000801b604051602001613bc19493929190615062565b6040516020818303038152906040528051906020012090506000613be58383613ff2565b905084604001516001600160a01b0316816001600160a01b0316148015613c1457506001600160a01b03811615155b9695505050505050565b60008080806001613c2f87826139b7565b94509050613c3d8782613ad1565b93509050613c4b8782613ad1565b602080890151604051929550929350600092613c6992879201614ed5565b60408051601f198184030181529082905280516020918201209250600091613ca1916001600160f81b03199189918691899101614e8c565b6040516020818303038152906040528051906020012060001c905087604001516001600160a01b0316816001600160a01b0316148015613ce95750606088015163ffffffff16155b9998505050505050505050565b600080613d068460016041613fd7565b9150506000613d378460200151604051602001613d239190614ec0565b6040516020818303038152906040526140ba565b613d4c613d47866060015161417b565b6140ba565b613d5c613d47876000015161417b565b604051602001613bc193929190614f46565b6000806000613d808560016041613fd7565b915091506000613d908684613ad1565b915050600085602001518660600151876000015184604051602001613db89493929190615062565b6040516020818303038152906040528051906020012090506000613ddc8483613ff2565b905086604001516001600160a01b0316816001600160a01b031614801561129557506001600160a01b03161515979650505050505050565b60006001600160601b03198216613b64613b5f85611d1f565b6000808260040190508084511015613e70576040805162461bcd60e51b81526020600482015260016024820152602b60f91b604482015290519081900360640190fd5b929092015192915050565b6000808260100190508084511015613e70576040805162461bcd60e51b81526020600482015260016024820152605760f81b604482015290519081900360640190fd5b6000808260140190508084511015613e70576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b60008282016001600160801b0380851690821610156121e5576040805162461bcd60e51b8152602060048201526002602482015261189960f11b604482015290519081900360640190fd5b6000808260200190508084511015613e70576040805162461bcd60e51b81526020600482015260016024820152605960f81b604482015290519081900360640190fd5b60008160140183511015613fce576040805162461bcd60e51b81526020600482015260016024820152605360f81b604482015290519081900360640190fd5b50016020015190565b60006060613fe68585856135f6565b93909201949293505050565b6000825160411461402e576040805162461bcd60e51b81526020600482015260016024820152600560fc1b604482015290519081900360640190fd5b60008060006020860151925060408601519150606086015160001a905060018582858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156140a5573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b6060600082516002026001600160401b03811180156140d857600080fd5b506040519080825280601f01601f191660200182016040528015614103576020820181803683370190505b5090506020830183518101602083015b8183101561417157825160f81c6f6665646362613938373635343332313060088260041c021c60f81b82526f66656463626139383736353433323130600882600f16021c60f81b600183015250600183019250600281019050614113565b5091949350505050565b604080516004808252818301909252606091610e479163ffffffff85169190849082602082018180368337505050602092830360080260ff169390931b918301919091525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b604080516060810182526000808252602082018190529181019190915290565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915290565b60408051608081018252600080825260208201819052918101829052606081019190915290565b600082601f830112614293578081fd5b813560206142a86142a3836158bc565b615899565b82815281810190858301855b858110156142dd576142cb898684358b0101614584565b845292840192908401906001016142b4565b5090979650505050505050565b600082601f8301126142fa578081fd5b8135602061430a6142a3836158bc565b82815281810190858301855b858110156142dd5781358801604080601f19838d03011215614336578889fd5b80518181016001600160401b03828210818311171561435157fe5b908352838901359080821115614365578b8cfd5b506143748d8a83870101614584565b825250614382828401614666565b81890152865250509284019290840190600101614316565b600082601f8301126143aa578081fd5b813560206143ba6142a3836158bc565b8281528181019085830160c0808602880185018910156143d8578687fd5b865b868110156143fe576143ec8a846145e8565b855293850193918101916001016143da565b509198975050505050505050565b600082601f83011261441c578081fd5b6040516102008082018281106001600160401b038211171561443a57fe5b604052818482810187101561444d578485fd5b8492505b601083101561447157803582526001929092019160209182019101614451565b509195945050505050565b600082601f83011261448c578081fd5b8135602061449c6142a3836158bc565b82815281810190858301838502870184018810156144b8578586fd5b855b858110156142dd578135845292840192908401906001016144ba565b600082601f8301126144e6578081fd5b813560206144f66142a3836158bc565b8281528181019085830183850287018401881015614512578586fd5b855b858110156142dd57813560ff8116811461452c578788fd5b84529284019290840190600101614514565b60008083601f84011261454f578182fd5b5081356001600160401b03811115614565578182fd5b60208301915083602082850101111561457d57600080fd5b9250929050565b600082601f830112614594578081fd5b81356001600160401b038111156145a757fe5b6145ba601f8201601f1916602001615899565b8181528460208386010111156145ce578283fd5b816020850160208301379081016020019190915292915050565b600060c082840312156145f9578081fd5b60405160c081018181106001600160401b038211171561461557fe5b60405290508061462483614666565b81526146326020840161467a565b602082015260408301356040820152606083013560608201526080830135608082015260a083013560a08201525092915050565b803563ffffffff81168114611f4557600080fd5b80356001600160401b0381168114611f4557600080fd5b6000602082840312156146a2578081fd5b81356121e581615905565b6000602082840312156146be578081fd5b81516121e581615905565b600080600080608085870312156146de578283fd5b84356146e981615905565b935060208501356146f981615905565b9250604085013561470981615905565b9396929550929360600135925050565b60008060006060848603121561472d578081fd5b833561473881615905565b9250602084013561474881615905565b915060408401356147588161591a565b809150509250925092565b60008060408385031215614775578182fd5b823561478081615905565b9150602083013561479081615905565b809150509250929050565b600080604083850312156147ad578182fd5b82356147b881615905565b915061399e60208401614666565b600060208083850312156147d8578182fd5b82356001600160401b03808211156147ee578384fd5b818501915085601f830112614801578384fd5b813561480f6142a3826158bc565b81815284810190848601875b84811015614898578135870160e080601f19838f0301121561483b578a8bfd5b604080518181018181108b8211171561485057fe5b825261485e8f858e016145e8565b8152918301359189831115614871578c8dfd5b61487f8f8d85870101614283565b818d01528752505050928701929087019060010161481b565b50909998505050505050505050565b6000602082840312156148b8578081fd5b81356001600160401b038111156148cd578182fd5b6136aa8482850161439a565b600080604083850312156148eb578182fd5b82356001600160401b0380821115614901578384fd5b61490d8683870161439a565b93506020850135915080821115614922578283fd5b908401906102808287031215614936578283fd5b61494060a0615899565b82358281111561494e578485fd5b61495a8882860161447c565b82525060208301358281111561496e578485fd5b61497a8882860161447c565b602083015250604083013582811115614991578485fd5b61499d8882860161447c565b6040830152506060830135828111156149b4578485fd5b6149c0888286016144d6565b6060830152506149d3876080850161440c565b60808201528093505050509250929050565b6000602082840312156149f6578081fd5b815180151581146121e5578182fd5b600060208284031215614a16578081fd5b5051919050565b60008060208385031215614a2f578182fd5b82356001600160401b03811115614a44578283fd5b614a508582860161453e565b90969095509350505050565b600080600060408486031215614a70578081fd5b83356001600160401b03811115614a85578182fd5b614a918682870161453e565b9094509250614aa4905060208501614666565b90509250925092565b60008060008060808587031215614ac2578182fd5b8435614acd81615905565b93506020850135614add81615905565b92506040850135614aed8161591a565b91506060850135614afd8161591a565b939692955090935050565b600080600060608486031215614b1c578081fd5b8335614b2781615905565b925060208401356001600160681b0381168114614b42578182fd5b9150604084013561475881615905565b6000806000806000806000806000806101e08b8d031215614b71578788fd5b614b7b8c8c6145e8565b995060c08b0135614b8b81615905565b9850614b9960e08c01614666565b9750614ba86101008c01614666565b96506101208b0135614bb98161591a565b9550614bc86101408c01614666565b94506101608b0135614bd981615905565b9350614be86101808c01614666565b92506101a08b013591506101c08b01356001600160401b03811115614c0b578182fd5b614c178d828e0161447c565b9150509295989b9194979a5092959850565b60008060e08385031215614c3b578182fd5b614c4584846145e8565b915060c08301356001600160401b0380821115614c60578283fd5b818501915085601f830112614c73578283fd5b81356020614c836142a3836158bc565b82815281810190858301875b85811015614d46578135880160c0818e03601f19011215614cae57898afd5b614cb860c0615899565b868201358152604082013589811115614ccf578b8cfd5b614cdd8f8983860101614584565b888301525060608201356040820152608082013589811115614cfd578b8cfd5b614d0b8f89838601016142ea565b606083015250614d1d60a08301614666565b6080820152614d2e60c08301614666565b60a08201528552509284019290840190600101614c8f565b50979a909950975050505050505050565b600060208284031215614d68578081fd5b81516121e58161591a565b600060208284031215614d84578081fd5b815161ffff811681146121e5578182fd5b600060208284031215614da6578081fd5b6121e582614666565b60008060408385031215614dc1578182fd5b61478083614666565b60008060408385031215614ddc578182fd5b6147b883614666565b60008060408385031215614df7578182fd5b614e008361467a565b915060208301356001600160401b03811115614e1a578182fd5b614e2685828601614283565b9150509250929050565b60601b6001600160601b0319169052565b6000815180845260208085019450808401835b83811015614e7057815187529582019590820190600101614e54565b509495945050505050565b60e01b6001600160e01b0319169052565b6001600160f81b031994909416845260609290921b6001600160601b03191660018401526015830152603582015260550190565b6001600160601b031991909116815260140190565b9182526001600160601b031916602082015260340190565b918252602082015260400190565b60008251614f0d8184602087016158d9565b9190910192915050565b60008351614f298184602088016158d9565b835190830190614f3d8183602088016158d9565b01949350505050565b60007f19457468657265756d205369676e6564204d6573736167653a0a31353200000082527f5265676973746572207a6b53796e63207075626b65793a0a0a00000000000000601d8301528451614fa48160368501602089016158d9565b600560f91b6036918401918201819052680dcdedcc6ca744060f60bb1b60378301528551614fd9816040850160208a016158d9565b60409201918201526d0c2c6c6deeadce840d2c8744060f60931b6041820152835161500b81604f8401602088016158d9565b61050560f11b604f92909101918201527f4f6e6c79207369676e2074686973206d65737361676520666f7220612074727560518201526b7374656420636c69656e742160a01b6071820152607d0195945050505050565b7f19457468657265756d205369676e6564204d6573736167653a0a36300000000081526001600160601b031994909416601c8501526001600160e01b031960e093841b811660308601529190921b166034830152603882015260580190565b60f89590951b6001600160f81b03191685526001600160e01b0319938416600186015260e09290921b909216600584015260809190911b6001600160801b031916600983015260601b6001600160601b0319166019820152602d0190565b6001600160f81b031960f88b901b1681526001600160e01b031960e08a811b821660018401526001600160601b031960608b901b16600584015288811b821660198401526001600160801b0319608089901b16601d84015286901b16602d820152600061518f6031830186614e30565b61519c6045830185614e7b565b50604981019190915260690198975050505050505050565b6001600160a01b0391909116815260200190565b6001600160a01b03861681526001600160401b03851660208201526000600c85106151ef57fe5b84604083015260a0606083015283518060a08401526152158160c08501602088016158d9565b608083019390935250601f91909101601f19160160c001949350505050565b6001600160a01b03968716815294909516602085015263ffffffff92831660408501529082166060840152608083015290911660a082015260c00190565b600061028080835261528681840189614e41565b905060208382038185015261529b8289614e41565b84810360408601528751808252828901935090820190845b818110156152d257845160ff16835293830193918301916001016152b3565b505084810360608601526152e68188614e41565b9350506080840191508460005b6010811015615310578151845292820192908201906001016152f3565b505050509695505050505050565b901515815260200190565b90815260200190565b6001600160a01b0394851681529290931660208301526001600160801b039081166040830152909116606082015260800190565b6020808252600190820152604360f81b604082015260600190565b6020808252600190820152604160f81b604082015260600190565b6020808252600190820152604760f81b604082015260600190565b6020808252600190820152606360f81b604082015260600190565b6020808252600190820152606760f81b604082015260600190565b6020808252600190820152602160f91b604082015260600190565b6020808252600190820152600760fc1b604082015260600190565b6020808252600190820152600960fb1b604082015260600190565b602080825260029082015261413160f01b604082015260600190565b6020808252600190820152607160f81b604082015260600190565b6020808252600190820152604560f81b604082015260600190565b6020808252600190820152603760f91b604082015260600190565b6020808252600190820152603760f81b604082015260600190565b6020808252600190820152606f60f81b604082015260600190565b602080825260059082015264065786531360dc1b604082015260600190565b6020808252600190820152601b60fa1b604082015260600190565b6020808252600190820152601160fa1b604082015260600190565b60208082526003908201526236b31960e91b604082015260600190565b6020808252600190820152600560fc1b604082015260600190565b6020808252600190820152602760f91b604082015260600190565b6020808252600190820152601560fa1b604082015260600190565b6020808252600290820152616f3160f01b604082015260600190565b6020808252600190820152601360fa1b604082015260600190565b6020808252600390820152626d663160e81b604082015260600190565b6020808252600190820152604960f81b604082015260600190565b6020808252600190820152602560f91b604082015260600190565b6020808252600190820152604b60f81b604082015260600190565b6020808252600190820152603b60f91b604082015260600190565b6020808252600190820152600d60fb1b604082015260600190565b6020808252600190820152606560f81b604082015260600190565b6020808252600190820152603560f91b604082015260600190565b6020808252600190820152603160f91b604082015260600190565b60208082526002908201526106f760f41b604082015260600190565b6020808252600190820152604f60f81b604082015260600190565b6020808252600190820152603560f81b604082015260600190565b6020808252600190820152603360f91b604082015260600190565b6020808252600190820152606d60f81b604082015260600190565b6020808252600190820152601b60f91b604082015260600190565b6020808252600190820152602360f91b604082015260600190565b6020808252600190820152606960f81b604082015260600190565b6020808252600190820152601960fa1b604082015260600190565b6020808252600190820152606b60f81b604082015260600190565b600060c08201905063ffffffff83511682526001600160401b03602084015116602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b6001600160801b0391909116815260200190565b61ffff91909116815260200190565b63ffffffff91909116815260200190565b63ffffffff9290921682526001600160a01b0316602082015260400190565b6001600160401b0391909116815260200190565b6040518181016001600160401b03811182821017156158b457fe5b604052919050565b60006001600160401b038211156158cf57fe5b5060209081020190565b60005b838110156158f45781810151838201526020016158dc565b838111156137cb5750506000910152565b6001600160a01b038116811461078557600080fd5b6001600160801b038116811461078557600080fdfe8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4a26469706673582212205d24e64f92160aa27539d4ac0bb5a901363da1fb80c2c822b0b94be21629f83d64736f6c63430007060033
Deployed Bytecode
0x6080604052600436106101ee5760003560e01c80637efcfe851161010d578063ab9b2adf116100a0578063c57b22be1161006f578063c57b22be14610524578063d514da5014610539578063e17376b514610559578063f223548714610579578063faf4d8cb1461058e576101ee565b8063ab9b2adf146104c4578063b0705b42146104e4578063b269b9ae14610425578063b4a8498c14610504576101ee565b80638ae20dc9116100dc5780638ae20dc91461044f5780638ee1a74e1461046f5780639ba0d1461461048f578063a7e7aacd146104af576101ee565b80637efcfe85146103e55780638398180814610405578063871b8ff1146104255780638773334c1461043a576101ee565b8063439fab91116101855780635aca41f6116101545780635aca41f61461035f578063647b59231461038c57806367708dae146103ae57806378b91e70146103d0576101ee565b8063439fab91146102df57806345269298146102ff578063505a75731461031f578063595a5ebc1461033f576101ee565b80632a3174f4116101c15780632a3174f4146102805780632d2da806146102a25780633b154b73146102b55780633e71e1e7146102ca576101ee565b806313d9787b146101f35780631d179643146102155780632539464514610235578063264c091214610255575b600080fd5b3480156101ff57600080fd5b5061021361020e366004614dca565b6105a3565b005b34801561022157600080fd5b50610213610230366004614b52565b6106e9565b34801561024157600080fd5b50610213610250366004614a1d565b6106fd565b34801561026157600080fd5b5061026a610731565b604051610277919061531e565b60405180910390f35b34801561028c57600080fd5b5061029561073a565b6040516102779190615329565b6102136102b0366004614691565b610740565b3480156102c157600080fd5b50610213610788565b3480156102d657600080fd5b5061021361078e565b3480156102eb57600080fd5b506102136102fa366004614a1d565b610798565b34801561030b57600080fd5b5061021361031a366004614c29565b6108c8565b34801561032b57600080fd5b5061021361033a366004614d95565b610aef565b34801561034b57600080fd5b5061021361035a366004614a5c565b610d6f565b34801561036b57600080fd5b5061037f61037a366004614763565b610d7c565b6040516102779190615832565b34801561039857600080fd5b506103a1610e4d565b6040516102779190615855565b3480156103ba57600080fd5b506103c3610e59565b6040516102779190615885565b3480156103dc57600080fd5b50610213610e68565b3480156103f157600080fd5b50610213610400366004614de5565b610e96565b34801561041157600080fd5b506102136104203660046148d9565b610ea2565b34801561043157600080fd5b506102136110d3565b34801561044657600080fd5b5061026a6110db565b34801561045b57600080fd5b5061029561046a36600461479b565b6110e5565b34801561047b57600080fd5b5061037f61048a366004614aad565b611102565b34801561049b57600080fd5b506102956104aa366004614d95565b6112a1565b3480156104bb57600080fd5b5061026a6112b3565b3480156104d057600080fd5b506102136104df366004614daf565b61136b565b3480156104f057600080fd5b506102136104ff3660046147c6565b611540565b34801561051057600080fd5b5061021361051f3660046148a7565b611775565b34801561053057600080fd5b506103c361177d565b34801561054557600080fd5b50610213610554366004614719565b611793565b34801561056557600080fd5b50610213610574366004614b08565b6119c2565b34801561058557600080fd5b506103a1611cd6565b34801561059a57600080fd5b506103a1611ce9565b60008051602061593083398151915254600181146105c057600080fd5b6002600080516020615930833981519152556105da611cfc565b62ffffff63ffffffff8416111561060c5760405162461bcd60e51b81526004016106039061567f565b60405180910390fd5b63ffffffff831662ffffff14156106355760405162461bcd60e51b815260040161060390615649565b63ffffffff821661ffff1080156106555750637ffffffe63ffffffff8316105b6106715760405162461bcd60e51b815260040161060390615589565b604080516101008101825263ffffffff80861682523360208301528416918101919091526000606082018190526080820181905260a0820181905260c0820181905260e082018190526106c382611d1f565b90506106d0600682611d60565b5050600160008051602061593083398151915255505050565b6106f1611ec8565b50505050505050505050565b600080516020615930833981519152546001811461071a57600080fd5b505060016000805160206159308339815191525550565b60095460ff1681565b60005b90565b6001600160a01b0381811614156107695760405162461bcd60e51b815260040161060390615553565b610771611cfc565b610785600061077f34611f03565b83611f4a565b50565b42601555565b610796611ec8565b565b6107a0611fdd565b60008080806107b1858701876146c9565b600280546001600160a01b038086166001600160a01b0319928316179092556003805483881690831617905560138054928516929091169190911790556040805160c0810182526000808252602082018190527fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47092820192909252606081018290526080810183905260a0810191909152939750919550935091506108558161202b565b60008052600d6020527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee556212750060148190556040517ff2b18f8abbd8a0d0c1fb8245146eedf5304887b12f6395b548ca238e054a1483916108b791615329565b60405180910390a150505050505050565b60008051602061593083398151915254600181146108e557600080fd5b6002600080516020615930833981519152556108ff611cfc565b600354604051634b18bd0f60e01b81526001600160a01b0390911690634b18bd0f9061092f9033906004016151b4565b60006040518083038186803b15801561094757600080fd5b505afa15801561095b573d6000803e3d6000fd5b505050506109688361202b565b600654600160601b900463ffffffff166000908152600d6020526040902054146109a45760405162461bcd60e51b81526004016106039061578e565b60005b82518163ffffffff161015610a6e576109d984848363ffffffff16815181106109cc57fe5b602002602001015161205b565b6020810151600c80546001600160401b03600160801b80830482169094011690920267ffffffffffffffff60801b199092169190911790559350610a1c8461202b565b845163ffffffff9081166000908152600d6020526040808220939093558651925192909116917f81a92942d0f9c33b897a438384c9c3d88be397776138efa3ba1a4fc8b62684249190a26001016109a7565b5081516006805463ffffffff600160601b80830482169094011690920263ffffffff60601b19909216919091179055600c546001600160401b03600160401b82048116600160801b909204161115610ad85760405162461bcd60e51b81526004016106039061569a565b600160008051602061593083398151915255505050565b6000805160206159308339815191525460018114610b0c57600080fd5b600260008051602061593083398151915281905563ffffffff808416600090815260126020908152604091829020825160c081018452815480861682526001600160a01b0364010000000082048116948301859052600160c01b909104861694820194909452600182015460608201529401549182166080850152600160a01b90910490911660a0830152610bb35760405162461bcd60e51b8152600401610603906156d0565b6003548151602083015160405163b79eb8c760e01b81526000936001600160a01b03169263b79eb8c792610be992600401615866565b60206040518083038186803b158015610c0157600080fd5b505afa158015610c15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3991906146ad565b9050806001600160a01b031663234ce590836020015184608001518560000151866040015187606001518860a001516040518763ffffffff1660e01b8152600401610c8996959493929190615234565b600060405180830381600087803b158015610ca357600080fd5b505af1158015610cb7573d6000803e3d6000fd5b5050505060a08201805163ffffffff90811660009081526011602052604080822080546001600160a01b0319166001600160a01b0387161790559251925192909116917f0b9f3586023bf754b8d962232407f7ac4d90fd19a1c4756c6619927abf0675609190a250505063ffffffff16600090815260126020526040812080546001600160e01b031916815560018082019290925560020180546001600160c01b031916905560008051602061593083398151915255565b610d77611ec8565b505050565b6000806001600160a01b03831615610e11576003546040516375698bb160e11b81526001600160a01b039091169063ead3176290610dbe9086906004016151b4565b60206040518083038186803b158015610dd657600080fd5b505afa158015610dea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0e9190614d73565b90505b60046000610e1f8684612189565b6001600160501b03191681526020810191909152604001600020546001600160801b03169150505b92915050565b600e5463ffffffff1681565b600c546001600160401b031681565b6000805460ff19166001908117909155429055601454601554610e8a916121a6565b42101561079657600080fd5b610e9e611ec8565b5050565b6000805160206159308339815191525460018114610ebf57600080fd5b600260008051602061593083398151915255610ed9611cfc565b600e5463ffffffff1660005b8451811015610fb35763ffffffff60018301166000908152600d60205260409020548551610f2690879084908110610f1957fe5b602002602001015161202b565b14610f435760405162461bcd60e51b8152600401610603906155a4565b8160010191506001600160fd1b03858281518110610f5d57fe5b602002602001015160a0015160001c166001600160fd1b0385604001518381518110610f8557fe5b60200260200101511614610fab5760405162461bcd60e51b8152600401610603906154c6565b600101610ee5565b506002548351602085015160608601516040808801516080890151915163054185eb60e51b81526000966001600160a01b03169563a830bd6095610fff95919490939192600401615272565b60206040518083038186803b15801561101757600080fd5b505afa15801561102b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104f91906149e5565b90508061106e5760405162461bcd60e51b815260040161060390615408565b60065463ffffffff600160601b909104811690831611156110a15760405162461bcd60e51b81526004016106039061545a565b50600e805463ffffffff191663ffffffff92909216919091179055505060016000805160206159308339815191525550565b6107966121ec565b60095460ff161590565b600a60209081526000928352604080842090915290825290205481565b60003330146111235760405162461bcd60e51b815260040161060390615707565b6040516370a0823160e01b81526000906001600160a01b038716906370a08231906111529030906004016151b4565b60206040518083038186803b15801561116a57600080fd5b505afa15801561117e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a29190614a05565b90506111b88686866001600160801b031661226f565b6111d45760405162461bcd60e51b815260040161060390615758565b6040516370a0823160e01b81526000906001600160a01b038816906370a08231906112039030906004016151b4565b60206040518083038186803b15801561121b57600080fd5b505afa15801561122f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112539190614a05565b905060006112618383612395565b9050846001600160801b031681111561128c5760405162461bcd60e51b8152600401610603906154ab565b61129581611f03565b98975050505050505050565b600d6020526000908152604090205481565b600c546001600160401b039081166000908152600f602052604081205490918291600160a01b900416431080159061130e5750600c546001600160401b039081166000908152600f6020526040902054600160a01b90041615155b905080156113615760095460ff16611357576009805460ff191660011790556040517fc71028c67eb0ef128ea270a59a674629e767d51c1af44ed6753fd2fad2c7b67790600090a15b600191505061073d565b600091505061073d565b600080516020615930833981519152546001811461138857600080fd5b6002600080516020615930833981519152556113a2611cfc565b62ffffff63ffffffff841611156113cb5760405162461bcd60e51b81526004016106039061567f565b63ffffffff831662ffffff14156113f45760405162461bcd60e51b815260040161060390615649565b60006001600160a01b03831661140c5750600061148f565b6003546040516375698bb160e11b81526001600160a01b039091169063ead317629061143c9086906004016151b4565b60206040518083038186803b15801561145457600080fd5b505afa158015611468573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148c9190614d73565b90505b604080516101008101825263ffffffff8616815233602082015261ffff8316918101919091526000606082018190526080820181905260a0820181905260c0820181905260e082018190526114e382611d1f565b90506114f0600682611d60565b60006114fc3385612189565b6001600160501b0319166000908152600460205260409020805460ff60801b191660ff60801b17905550506001600080516020615930833981519152555050505050565b600080516020615930833981519152546001811461155d57600080fd5b600260008051602061593083398151915255611577611cfc565b600354604051634b18bd0f60e01b81526001600160a01b0390911690634b18bd0f906115a79033906004016151b4565b60006040518083038186803b1580156115bf57600080fd5b505afa1580156115d3573d6000803e3d6000fd5b50508351600092509050815b8163ffffffff168163ffffffff16101561169857611616858263ffffffff168151811061160857fe5b6020026020010151826123bb565b848163ffffffff168151811061162857fe5b6020026020010151600001516020015183019250848163ffffffff168151811061164e57fe5b6020026020010151600001516000015163ffffffff167f0cdbd8bd7813095001c5fe7917bd69d834dc01db7c1dfcf52ca135bd2038441360405160405180910390a26001016115df565b50600c805467ffffffffffffffff60401b1967ffffffffffffffff60801b1967ffffffffffffffff1983166001600160401b039384168701841617908116600160801b918290048416879003841690910217908116600160401b918290048316869003909216810291909117909155600680546bffffffff00000000000000001981169083900463ffffffff9081168501811684029190911791829055600e5481169290910416111561175d5760405162461bcd60e51b815260040161060390615490565b50506001600080516020615930833981519152555050565b610785611ec8565b600c54600160401b90046001600160401b031681565b60008051602061593083398151915254600181146117b057600080fd5b6002600080516020615930833981519152556001600160a01b038316611867576117dc600083866126e0565b6000846001600160a01b0316836001600160801b03166040516117fe9061073d565b60006040518083038185875af1925050503d806000811461183b576040519150601f19603f3d011682016040523d82523d6000602084013e611840565b606091505b50509050806118615760405162461bcd60e51b8152600401610603906157a9565b506119aa565b6003546040516375698bb160e11b81526000916001600160a01b03169063ead31762906118989087906004016151b4565b60206040518083038186803b1580156118b057600080fd5b505afa1580156118c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e89190614d73565b905060006118f68683612189565b6001600160501b031981166000908152600460208190526040808320549051634770d3a760e11b81529394506001600160801b0316923091638ee1a74e91611946918b918d918c91899101615332565b602060405180830381600087803b15801561196057600080fd5b505af1158015611974573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119989190614d57565b90506119a584828a6126e0565b505050505b60016000805160206159308339815191525550505050565b60008051602061593083398151915254600181146119df57600080fd5b6002600080516020615930833981519152556001600160a01b038281161415611a1a5760405162461bcd60e51b815260040161060390615553565b611a22611cfc565b6003546040516375698bb160e11b81526000916001600160a01b03169063ead3176290611a539088906004016151b4565b60206040518083038186803b158015611a6b57600080fd5b505afa158015611a7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aa39190614d73565b60035460405163f3a65bf960e01b81529192506001600160a01b03169063f3a65bf990611ad4908490600401615846565b60206040518083038186803b158015611aec57600080fd5b505afa158015611b00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b2491906149e5565b15611b415760405162461bcd60e51b8152600401610603906156b5565b6040516370a0823160e01b81526000906001600160a01b038716906370a0823190611b709030906004016151b4565b60206040518083038186803b158015611b8857600080fd5b505afa158015611b9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc09190614a05565b9050611be8863330611bda896001600160681b0316611f03565b6001600160801b031661278a565b611c045760405162461bcd60e51b8152600401610603906153b7565b6040516370a0823160e01b81526000906001600160a01b038816906370a0823190611c339030906004016151b4565b60206040518083038186803b158015611c4b57600080fd5b505afa158015611c5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c839190614a05565b90506000611c99611c948385612395565b611f03565b90506001600160681b036001600160801b0382161115611ccb5760405162461bcd60e51b815260040161060390615366565b6119a5848288611f4a565b600654600160401b900463ffffffff1681565b600654600160601b900463ffffffff1681565b60095460ff16156107965760405162461bcd60e51b8152600401610603906155c0565b6060600682516020808501516040808701519051611d4a95949360009182918291829182910161511f565b6040516020818303038152906040529050919050565b600c544361438001906001600160401b03808216600160401b90920416016000611d89846128b6565b90506040518060600160405280826bffffffffffffffffffffffff19168152602001846001600160401b0316815260200186600b811115611dc657fe5b90526001600160401b038084166000908152600f60209081526040918290208451815492860151909416600160a01b0267ffffffffffffffff60a01b1960609590951c6001600160a01b03199093169290921793909316178083559083015190829060ff60e01b1916600160e01b83600b811115611e4057fe5b02179055509050507fd0943372c08b438a88d4b39d77216901079eda9ca59d45349841c099083b683033838787876001600160401b0316604051611e889594939291906151c8565b60405180910390a15050600c805460016001600160401b03600160401b808404821692909201160267ffffffffffffffff60401b19909116179055505050565b6013546040516001600160a01b039091169036600082376000803683855af43d806000843e818015611ef8578184f35b8184fd5b5050505050565b6000600160801b8210611f42576040805162461bcd60e51b8152602060048201526002602482015261189b60f11b604482015290519081900360640190fd5b50805b919050565b60408051608081018252600080825261ffff861660208301526001600160801b038516928201929092526001600160a01b038316606082015290611f8d826128c4565b9050611f9a600182611d60565b8461ffff167f8f5f51448394699ad6a3b80cdadf4ec68c5d724c8c3fea09bea55b3c2d0e2dd085604051611fce9190615832565b60405180910390a25050505050565b600080516020615930833981519152805460019091558015610785576040805162461bcd60e51b815260206004820152600260248201526118a160f11b604482015290519081900360640190fd5b60008160405160200161203e91906157df565b604051602081830303815290604052805190602001209050919050565b6120636141c3565b826000015160010163ffffffff16826080015163ffffffff16146120995760405162461bcd60e51b815260040161060390615722565b8260600151826040015110156120c15760405162461bcd60e51b8152600401610603906153d2565b60408201516000906120d64262015180612395565b1115905060006120e8426103846121a6565b8460400151111590508180156120fb5750805b6121175760405162461bcd60e51b815260040161060390615664565b50506000806000612127856128eb565b925092509250600061213a878784612cd3565b6040805160c0810182526080808a015163ffffffff1682526001600160401b039096166020820152808201969096528701516060860152865193850193909352505060a0820152905092915050565b60a01b61ffff60a01b166001600160a01b03919091161760501b90565b6000828201838110156121e5576040805162461bcd60e51b81526020600482015260026024820152610c4d60f21b604482015290519081900360640190fd5b9392505050565b6000805460ff191681556001556212750060148190556040517ff2b18f8abbd8a0d0c1fb8245146eedf5304887b12f6395b548ca238e054a14839161223091615329565b60405180910390a1600060158190555b600f811015612267576000818152601660205260409020805460ff19169055600101612240565b506000601755565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17815292518251600094859485948a16939092909182918083835b602083106122ed5780518252601f1990920191602091820191016122ce565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461234f576040519150601f19603f3d011682016040523d82523d6000602084013e612354565b606091505b5091509150600081516000148061237e575081806020019051602081101561237b57600080fd5b50515b905082801561238a5750805b979650505050505050565b60006121e58383604051806040016040528060018152602001603b60f91b815250612f16565b81515163ffffffff166000908152600d602052604090205482516123de9061202b565b146123fb5760405162461bcd60e51b8152600401610603906154e1565b600654825151600160401b90910463ffffffff908116830160010181169116146124375760405162461bcd60e51b8152600401610603906157c4565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060005b8360200151518163ffffffff1610156126bb57600084602001518263ffffffff168151811061248657fe5b6020026020010151905060008160008151811061249f57fe5b016020015160f81c600b8111156124b257fe5b9050600381600b8111156124c257fe5b14156125245760006124d383612fad565b905061ffff63ffffffff16816000015163ffffffff1611156125075760405162461bcd60e51b8152600401610603906155db565b61251e816000015182604001518360200151613008565b506126a2565b600881600b81111561253257fe5b1415612577576000612543836131d4565b905061ffff63ffffffff16816000015163ffffffff1611156125075760405162461bcd60e51b815260040161060390615536565b600681600b81111561258557fe5b1415612660576000612596836131e8565b905061ffff63ffffffff16816040015163ffffffff16116125cd576125c8816040015182602001518360600151613008565b61251e565b80606001516001600160801b03166001141561251e5760006040518060c00160405280836080015163ffffffff1681526020018360a001516001600160a01b031681526020018360c0015163ffffffff1681526020018360e00151815260200183602001516001600160a01b03168152602001836040015163ffffffff168152509050612659816132d0565b50506126a2565b600a81600b81111561266e57fe5b141561268a57600061267f83613529565b905061251e816132d0565b60405162461bcd60e51b815260040161060390615500565b6126ac84836135c0565b9350505080600101905061245b565b508251604001518114610d775760405162461bcd60e51b81526004016106039061573d565b60006126ec8285612189565b6001600160501b031981166000908152600460205260409020549091506001600160801b031661271c81856135cf565b6001600160501b031983166000908152600460205260409081902080546001600160801b0319166001600160801b0393909316929092179091555161ffff8616907ff4bf32c167ee6e782944cd1db8174729b46adcd3bc732e282cc4a8079393315490611fce908790615832565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17815292518251600094859485948b16939092909182918083835b602083106128105780518252601f1990920191602091820191016127f1565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612872576040519150601f19603f3d011682016040523d82523d6000602084013e612877565b606091505b509150915060008151600014806128a1575081806020019051602081101561289e57600080fd5b50515b90508280156112955750979650505050505050565b805160209091012060601b90565b6060600160208084015160408086015160608701519151611d4a95946000949391016150c1565b6020810151600c5481517fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470926000926060926001600160401b03808316600160801b909304169190910190600a9006156129575760405162461bcd60e51b815260040161060390615381565b8151600a90046001600160401b038111801561297257600080fd5b506040519080825280601f01601f19166020018201604052801561299d576020820181803683370190505b50925060005b866060015151811015612cc9576000876060015182815181106129c257fe5b602002602001015190506000816020015163ffffffff169050845181106129fb5760405162461bcd60e51b81526004016106039061543e565b600a810615612a1c5760405162461bcd60e51b8152600401610603906153ed565b6000600a82049050868181518110612a3057fe5b01602001516001600160f81b03191615612a5c5760405162461bcd60e51b815260040161060390615366565b600160f81b878281518110612a6d57fe5b60200101906001600160f81b031916908160001a9053506000868381518110612a9257fe5b016020015160f81c600b811115612aa557fe5b9050600181600b811115612ab557fe5b1415612af1576000612ac98885603c6135f6565b90506000612ad6826136b2565b9050612ae4818c8a0161373b565b5050600190980197612cba565b600781600b811115612aff57fe5b1415612be4576000612b138885603c6135f6565b90506000612b20826137d1565b86515190915015612b60576000612b3b876000015183613841565b905080612b5a5760405162461bcd60e51b81526004016106039061551b565b50612bdd565b60008160200151604051602001612b779190614ec0565b60408051601f198184030181529181528151602092830120848201516001600160a01b03166000908152600a8452828120606087015163ffffffff16825290935291205414905080612bdb5760405162461bcd60e51b815260040161060390615475565b505b5050612cba565b6060600382600b811115612bf457fe5b1415612c0d57612c068885603c6135f6565b9050612cac565b600882600b811115612c1b57fe5b1415612c2d57612c068885603c6135f6565b600a82600b811115612c3b57fe5b1415612c4d57612c06888560646135f6565b600682600b811115612c5b57fe5b1415612c9457612c6d8885606e6135f6565b90506000612c7a826131e8565b9050612c88818c8a016138fe565b50600190990198612cac565b60405162461bcd60e51b815260040161060390615773565b612cb68b826135c0565b9a50505b505050508060010190506129a3565b5050509193909250565b6000806002846080015163ffffffff168560a0015163ffffffff16604051602001612cff929190614eed565b60408051601f1981840301815290829052612d1991614efb565b602060405180830381855afa158015612d36573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612d599190614a05565b90506002818660800151604051602001612d74929190614eed565b60408051601f1981840301815290829052612d8e91614efb565b602060405180830381855afa158015612dab573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612dce9190614a05565b8451604051919250600291612de7918491602001614eed565b60408051601f1981840301815290829052612e0191614efb565b602060405180830381855afa158015612e1e573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612e419190614a05565b90506002818560400151604051602001612e5c929190614eed565b60408051601f1981840301815290829052612e7691614efb565b602060405180830381855afa158015612e93573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612eb69190614a05565b90506000846020015184604051602001612ed1929190614f17565b60405160208183030381529060405290506040518151838352602082602083018560025afa818452808015612f0557612f07565bfe5b50509051979650505050505050565b60008184841115612fa55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612f6a578181015183820152602001612f52565b50505050905090810190601f168015612f975780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b612fb56141f8565b6005612fc1838261398e565b63ffffffff1683529050612fd583826139a7565b6001600160801b031660208401526002019050612ff283826139b7565b6001600160a01b03166040840152509092915050565b60006130148385612189565b9050600061ffff851661303d5783613035816001600160801b0386166139c7565b91505061314f565b6003546040516310603dad60e01b81526000916001600160a01b0316906310603dad9061306e908990600401615846565b60206040518083038186803b15801561308657600080fd5b505afa15801561309a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130be91906146ad565b604051634770d3a760e11b81529091503090638ee1a74e90620186a0906130ef9085908a908a908190600401615332565b602060405180830381600088803b15801561310957600080fd5b5087f19350505050801561313a575060408051601f3d908101601f1916820190925261313791810190614d57565b60015b613147576000915061314d565b50600191505b505b8015613196578461ffff167ff4bf32c167ee6e782944cd1db8174729b46adcd3bc732e282cc4a80793933154846040516131899190615832565b60405180910390a2611efc565b6131a08284613a33565b8461ffff167fd19cf67bbb6c320849f41b650b1179fb06a3f104451c75109c3b006a385c168884604051611fce9190615832565b6131dc6141f8565b6009612fc1838261398e565b6131f0614218565b60016131fc838261398e565b63ffffffff168352905061321083826139b7565b6001600160a01b03166020840152905061322a838261398e565b63ffffffff166040840152905061324183826139a7565b6001600160801b03166060840152905061325b838261398e565b63ffffffff166080840152905061327283826139b7565b6001600160a01b031660a0840152905061328c838261398e565b63ffffffff1660c084015290506132a38382613ad1565b60e08401529050606981146132ca5760405162461bcd60e51b8152600401610603906156ec565b50919050565b6003548151602083015160405163b79eb8c760e01b81526000936001600160a01b03169263b79eb8c79261330692600401615866565b60206040518083038186803b15801561331e57600080fd5b505afa158015613332573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335691906146ad565b9050806001600160a01b031663234ce590620493e0846020015185608001518660000151876040015188606001518960a001516040518863ffffffff1660e01b81526004016133aa96959493929190615234565b600060405180830381600088803b1580156133c457600080fd5b5087f1935050505080156133d6575060015b6134c25760a08201805163ffffffff90811660009081526012602090815260408083208751815493890151838a015163ffffffff1990951691871691909117640100000000600160c01b0319166401000000006001600160a01b03928316021763ffffffff60c01b1916600160c01b948716949094029390931781556060880151600182015560808801516002909101805496516001600160a01b0319909716919093161763ffffffff60a01b1916600160a01b9590941694850293909317905590517f1d19de6753fc463923c3d90b6fb45aca536732ed7c5ca377d4b3f63dfbe4d8199190a2610e9e565b60a08201805163ffffffff90811660009081526011602052604080822080546001600160a01b0319166001600160a01b0387161790559251925192909116917f0b9f3586023bf754b8d962232407f7ac4d90fd19a1c4756c6619927abf0675609190a25050565b6135316141c3565b600561353d838261398e565b63ffffffff168352905061355183826139b7565b6001600160a01b03166020840152905061356b838261398e565b63ffffffff16604084015290506135828382613ad1565b6060840152905061359383826139b7565b6001600160a01b0316608084015290506135ad838261398e565b63ffffffff1660a0840152509092915050565b80519181526020909101902090565b60006121e5838360405180604001604052806002815260200161616160f01b815250613ae1565b606081830184511015613634576040805162461bcd60e51b81526020600482015260016024820152602d60f91b604482015290519081900360640190fd5b6000826001600160401b038111801561364c57600080fd5b506040519080825280601f01601f191660200182016040528015613677576020820181803683370190505b50905082156136aa57602081018381016020860187015b818310156136a657805183526020928301920161368e565b5050505b949350505050565b6136ba61425c565b60016136c6838261398e565b63ffffffff16835290506136da838261398e565b63ffffffff16602084015290506136f183826139a7565b6001600160801b03166040840152905061370b83826139b7565b6001600160a01b031660608401529050602d81146132ca5760405162461bcd60e51b81526004016106039061556e565b6001600160401b0381166000908152600f6020526040902054600160e01b900460ff16600181600b81111561376c57fe5b146137895760405162461bcd60e51b815260040161060390615423565b6001600160401b0382166000908152600f602052604090205460601b6137af8482613b46565b6137cb5760405162461bcd60e51b8152600401610603906155f8565b50505050565b6137d961425c565b60016137e5838261398e565b63ffffffff16835290506137f98382613b76565b6001600160601b0319166020840152905061381483826139b7565b6001600160a01b03166040840152905061382e838261398e565b63ffffffff166060840152509092915050565b6000808360008151811061385157fe5b016020015160f81c600381111561386457fe5b9050600081600381111561387457fe5b141561388c576138848484613b86565b915050610e47565b600181600381111561389a57fe5b14156138aa576138848484613c1e565b60028160038111156138b857fe5b14156138c8576138848484613cf6565b60038160038111156138d657fe5b14156138e6576138848484613d6e565b60405162461bcd60e51b81526004016106039061539c565b6001600160401b0381166000908152600f6020526040902054600160e01b900460ff16600681600b81111561392f57fe5b1461394c5760405162461bcd60e51b815260040161060390615613565b6001600160401b0382166000908152600f602052604090205460601b6139728482613e14565b6137cb5760405162461bcd60e51b81526004016106039061562e565b60048101600061399e8484613e2d565b90509250929050565b60108101600061399e8484613e7b565b60148101600061399e8484613ebe565b600080836001600160a01b0316620186a0846040516139e59061073d565b600060405180830381858888f193505050503d8060008114613a23576040519150601f19603f3d011682016040523d82523d6000602084013e613a28565b606091505b509095945050505050565b6001600160501b03198216600090815260046020526040908190205481518083019092526001600160801b03169080613a6c8385613f01565b6001600160801b03908116825260ff60209283018190526001600160501b031990961660009081526004835260409020835181549490930151909616600160801b0260ff60801b19929091166001600160801b03199093169290921716179092555050565b60208101600061399e8484613f4c565b6000836001600160801b0316836001600160801b031611158290612fa55760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315612f6a578181015183820152602001612f52565b60006001600160601b03198216613b64613b5f856128c4565b6128b6565b6001600160601b031916149392505050565b60148101600061399e8484613f8f565b600080613b968460016041613fd7565b91505060008360200151846060015185600001516000801b604051602001613bc19493929190615062565b6040516020818303038152906040528051906020012090506000613be58383613ff2565b905084604001516001600160a01b0316816001600160a01b0316148015613c1457506001600160a01b03811615155b9695505050505050565b60008080806001613c2f87826139b7565b94509050613c3d8782613ad1565b93509050613c4b8782613ad1565b602080890151604051929550929350600092613c6992879201614ed5565b60408051601f198184030181529082905280516020918201209250600091613ca1916001600160f81b03199189918691899101614e8c565b6040516020818303038152906040528051906020012060001c905087604001516001600160a01b0316816001600160a01b0316148015613ce95750606088015163ffffffff16155b9998505050505050505050565b600080613d068460016041613fd7565b9150506000613d378460200151604051602001613d239190614ec0565b6040516020818303038152906040526140ba565b613d4c613d47866060015161417b565b6140ba565b613d5c613d47876000015161417b565b604051602001613bc193929190614f46565b6000806000613d808560016041613fd7565b915091506000613d908684613ad1565b915050600085602001518660600151876000015184604051602001613db89493929190615062565b6040516020818303038152906040528051906020012090506000613ddc8483613ff2565b905086604001516001600160a01b0316816001600160a01b031614801561129557506001600160a01b03161515979650505050505050565b60006001600160601b03198216613b64613b5f85611d1f565b6000808260040190508084511015613e70576040805162461bcd60e51b81526020600482015260016024820152602b60f91b604482015290519081900360640190fd5b929092015192915050565b6000808260100190508084511015613e70576040805162461bcd60e51b81526020600482015260016024820152605760f81b604482015290519081900360640190fd5b6000808260140190508084511015613e70576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b60008282016001600160801b0380851690821610156121e5576040805162461bcd60e51b8152602060048201526002602482015261189960f11b604482015290519081900360640190fd5b6000808260200190508084511015613e70576040805162461bcd60e51b81526020600482015260016024820152605960f81b604482015290519081900360640190fd5b60008160140183511015613fce576040805162461bcd60e51b81526020600482015260016024820152605360f81b604482015290519081900360640190fd5b50016020015190565b60006060613fe68585856135f6565b93909201949293505050565b6000825160411461402e576040805162461bcd60e51b81526020600482015260016024820152600560fc1b604482015290519081900360640190fd5b60008060006020860151925060408601519150606086015160001a905060018582858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156140a5573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b6060600082516002026001600160401b03811180156140d857600080fd5b506040519080825280601f01601f191660200182016040528015614103576020820181803683370190505b5090506020830183518101602083015b8183101561417157825160f81c6f6665646362613938373635343332313060088260041c021c60f81b82526f66656463626139383736353433323130600882600f16021c60f81b600183015250600183019250600281019050614113565b5091949350505050565b604080516004808252818301909252606091610e479163ffffffff85169190849082602082018180368337505050602092830360080260ff169390931b918301919091525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b604080516060810182526000808252602082018190529181019190915290565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915290565b60408051608081018252600080825260208201819052918101829052606081019190915290565b600082601f830112614293578081fd5b813560206142a86142a3836158bc565b615899565b82815281810190858301855b858110156142dd576142cb898684358b0101614584565b845292840192908401906001016142b4565b5090979650505050505050565b600082601f8301126142fa578081fd5b8135602061430a6142a3836158bc565b82815281810190858301855b858110156142dd5781358801604080601f19838d03011215614336578889fd5b80518181016001600160401b03828210818311171561435157fe5b908352838901359080821115614365578b8cfd5b506143748d8a83870101614584565b825250614382828401614666565b81890152865250509284019290840190600101614316565b600082601f8301126143aa578081fd5b813560206143ba6142a3836158bc565b8281528181019085830160c0808602880185018910156143d8578687fd5b865b868110156143fe576143ec8a846145e8565b855293850193918101916001016143da565b509198975050505050505050565b600082601f83011261441c578081fd5b6040516102008082018281106001600160401b038211171561443a57fe5b604052818482810187101561444d578485fd5b8492505b601083101561447157803582526001929092019160209182019101614451565b509195945050505050565b600082601f83011261448c578081fd5b8135602061449c6142a3836158bc565b82815281810190858301838502870184018810156144b8578586fd5b855b858110156142dd578135845292840192908401906001016144ba565b600082601f8301126144e6578081fd5b813560206144f66142a3836158bc565b8281528181019085830183850287018401881015614512578586fd5b855b858110156142dd57813560ff8116811461452c578788fd5b84529284019290840190600101614514565b60008083601f84011261454f578182fd5b5081356001600160401b03811115614565578182fd5b60208301915083602082850101111561457d57600080fd5b9250929050565b600082601f830112614594578081fd5b81356001600160401b038111156145a757fe5b6145ba601f8201601f1916602001615899565b8181528460208386010111156145ce578283fd5b816020850160208301379081016020019190915292915050565b600060c082840312156145f9578081fd5b60405160c081018181106001600160401b038211171561461557fe5b60405290508061462483614666565b81526146326020840161467a565b602082015260408301356040820152606083013560608201526080830135608082015260a083013560a08201525092915050565b803563ffffffff81168114611f4557600080fd5b80356001600160401b0381168114611f4557600080fd5b6000602082840312156146a2578081fd5b81356121e581615905565b6000602082840312156146be578081fd5b81516121e581615905565b600080600080608085870312156146de578283fd5b84356146e981615905565b935060208501356146f981615905565b9250604085013561470981615905565b9396929550929360600135925050565b60008060006060848603121561472d578081fd5b833561473881615905565b9250602084013561474881615905565b915060408401356147588161591a565b809150509250925092565b60008060408385031215614775578182fd5b823561478081615905565b9150602083013561479081615905565b809150509250929050565b600080604083850312156147ad578182fd5b82356147b881615905565b915061399e60208401614666565b600060208083850312156147d8578182fd5b82356001600160401b03808211156147ee578384fd5b818501915085601f830112614801578384fd5b813561480f6142a3826158bc565b81815284810190848601875b84811015614898578135870160e080601f19838f0301121561483b578a8bfd5b604080518181018181108b8211171561485057fe5b825261485e8f858e016145e8565b8152918301359189831115614871578c8dfd5b61487f8f8d85870101614283565b818d01528752505050928701929087019060010161481b565b50909998505050505050505050565b6000602082840312156148b8578081fd5b81356001600160401b038111156148cd578182fd5b6136aa8482850161439a565b600080604083850312156148eb578182fd5b82356001600160401b0380821115614901578384fd5b61490d8683870161439a565b93506020850135915080821115614922578283fd5b908401906102808287031215614936578283fd5b61494060a0615899565b82358281111561494e578485fd5b61495a8882860161447c565b82525060208301358281111561496e578485fd5b61497a8882860161447c565b602083015250604083013582811115614991578485fd5b61499d8882860161447c565b6040830152506060830135828111156149b4578485fd5b6149c0888286016144d6565b6060830152506149d3876080850161440c565b60808201528093505050509250929050565b6000602082840312156149f6578081fd5b815180151581146121e5578182fd5b600060208284031215614a16578081fd5b5051919050565b60008060208385031215614a2f578182fd5b82356001600160401b03811115614a44578283fd5b614a508582860161453e565b90969095509350505050565b600080600060408486031215614a70578081fd5b83356001600160401b03811115614a85578182fd5b614a918682870161453e565b9094509250614aa4905060208501614666565b90509250925092565b60008060008060808587031215614ac2578182fd5b8435614acd81615905565b93506020850135614add81615905565b92506040850135614aed8161591a565b91506060850135614afd8161591a565b939692955090935050565b600080600060608486031215614b1c578081fd5b8335614b2781615905565b925060208401356001600160681b0381168114614b42578182fd5b9150604084013561475881615905565b6000806000806000806000806000806101e08b8d031215614b71578788fd5b614b7b8c8c6145e8565b995060c08b0135614b8b81615905565b9850614b9960e08c01614666565b9750614ba86101008c01614666565b96506101208b0135614bb98161591a565b9550614bc86101408c01614666565b94506101608b0135614bd981615905565b9350614be86101808c01614666565b92506101a08b013591506101c08b01356001600160401b03811115614c0b578182fd5b614c178d828e0161447c565b9150509295989b9194979a5092959850565b60008060e08385031215614c3b578182fd5b614c4584846145e8565b915060c08301356001600160401b0380821115614c60578283fd5b818501915085601f830112614c73578283fd5b81356020614c836142a3836158bc565b82815281810190858301875b85811015614d46578135880160c0818e03601f19011215614cae57898afd5b614cb860c0615899565b868201358152604082013589811115614ccf578b8cfd5b614cdd8f8983860101614584565b888301525060608201356040820152608082013589811115614cfd578b8cfd5b614d0b8f89838601016142ea565b606083015250614d1d60a08301614666565b6080820152614d2e60c08301614666565b60a08201528552509284019290840190600101614c8f565b50979a909950975050505050505050565b600060208284031215614d68578081fd5b81516121e58161591a565b600060208284031215614d84578081fd5b815161ffff811681146121e5578182fd5b600060208284031215614da6578081fd5b6121e582614666565b60008060408385031215614dc1578182fd5b61478083614666565b60008060408385031215614ddc578182fd5b6147b883614666565b60008060408385031215614df7578182fd5b614e008361467a565b915060208301356001600160401b03811115614e1a578182fd5b614e2685828601614283565b9150509250929050565b60601b6001600160601b0319169052565b6000815180845260208085019450808401835b83811015614e7057815187529582019590820190600101614e54565b509495945050505050565b60e01b6001600160e01b0319169052565b6001600160f81b031994909416845260609290921b6001600160601b03191660018401526015830152603582015260550190565b6001600160601b031991909116815260140190565b9182526001600160601b031916602082015260340190565b918252602082015260400190565b60008251614f0d8184602087016158d9565b9190910192915050565b60008351614f298184602088016158d9565b835190830190614f3d8183602088016158d9565b01949350505050565b60007f19457468657265756d205369676e6564204d6573736167653a0a31353200000082527f5265676973746572207a6b53796e63207075626b65793a0a0a00000000000000601d8301528451614fa48160368501602089016158d9565b600560f91b6036918401918201819052680dcdedcc6ca744060f60bb1b60378301528551614fd9816040850160208a016158d9565b60409201918201526d0c2c6c6deeadce840d2c8744060f60931b6041820152835161500b81604f8401602088016158d9565b61050560f11b604f92909101918201527f4f6e6c79207369676e2074686973206d65737361676520666f7220612074727560518201526b7374656420636c69656e742160a01b6071820152607d0195945050505050565b7f19457468657265756d205369676e6564204d6573736167653a0a36300000000081526001600160601b031994909416601c8501526001600160e01b031960e093841b811660308601529190921b166034830152603882015260580190565b60f89590951b6001600160f81b03191685526001600160e01b0319938416600186015260e09290921b909216600584015260809190911b6001600160801b031916600983015260601b6001600160601b0319166019820152602d0190565b6001600160f81b031960f88b901b1681526001600160e01b031960e08a811b821660018401526001600160601b031960608b901b16600584015288811b821660198401526001600160801b0319608089901b16601d84015286901b16602d820152600061518f6031830186614e30565b61519c6045830185614e7b565b50604981019190915260690198975050505050505050565b6001600160a01b0391909116815260200190565b6001600160a01b03861681526001600160401b03851660208201526000600c85106151ef57fe5b84604083015260a0606083015283518060a08401526152158160c08501602088016158d9565b608083019390935250601f91909101601f19160160c001949350505050565b6001600160a01b03968716815294909516602085015263ffffffff92831660408501529082166060840152608083015290911660a082015260c00190565b600061028080835261528681840189614e41565b905060208382038185015261529b8289614e41565b84810360408601528751808252828901935090820190845b818110156152d257845160ff16835293830193918301916001016152b3565b505084810360608601526152e68188614e41565b9350506080840191508460005b6010811015615310578151845292820192908201906001016152f3565b505050509695505050505050565b901515815260200190565b90815260200190565b6001600160a01b0394851681529290931660208301526001600160801b039081166040830152909116606082015260800190565b6020808252600190820152604360f81b604082015260600190565b6020808252600190820152604160f81b604082015260600190565b6020808252600190820152604760f81b604082015260600190565b6020808252600190820152606360f81b604082015260600190565b6020808252600190820152606760f81b604082015260600190565b6020808252600190820152602160f91b604082015260600190565b6020808252600190820152600760fc1b604082015260600190565b6020808252600190820152600960fb1b604082015260600190565b602080825260029082015261413160f01b604082015260600190565b6020808252600190820152607160f81b604082015260600190565b6020808252600190820152604560f81b604082015260600190565b6020808252600190820152603760f91b604082015260600190565b6020808252600190820152603760f81b604082015260600190565b6020808252600190820152606f60f81b604082015260600190565b602080825260059082015264065786531360dc1b604082015260600190565b6020808252600190820152601b60fa1b604082015260600190565b6020808252600190820152601160fa1b604082015260600190565b60208082526003908201526236b31960e91b604082015260600190565b6020808252600190820152600560fc1b604082015260600190565b6020808252600190820152602760f91b604082015260600190565b6020808252600190820152601560fa1b604082015260600190565b6020808252600290820152616f3160f01b604082015260600190565b6020808252600190820152601360fa1b604082015260600190565b6020808252600390820152626d663160e81b604082015260600190565b6020808252600190820152604960f81b604082015260600190565b6020808252600190820152602560f91b604082015260600190565b6020808252600190820152604b60f81b604082015260600190565b6020808252600190820152603b60f91b604082015260600190565b6020808252600190820152600d60fb1b604082015260600190565b6020808252600190820152606560f81b604082015260600190565b6020808252600190820152603560f91b604082015260600190565b6020808252600190820152603160f91b604082015260600190565b60208082526002908201526106f760f41b604082015260600190565b6020808252600190820152604f60f81b604082015260600190565b6020808252600190820152603560f81b604082015260600190565b6020808252600190820152603360f91b604082015260600190565b6020808252600190820152606d60f81b604082015260600190565b6020808252600190820152601b60f91b604082015260600190565b6020808252600190820152602360f91b604082015260600190565b6020808252600190820152606960f81b604082015260600190565b6020808252600190820152601960fa1b604082015260600190565b6020808252600190820152606b60f81b604082015260600190565b600060c08201905063ffffffff83511682526001600160401b03602084015116602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b6001600160801b0391909116815260200190565b61ffff91909116815260200190565b63ffffffff91909116815260200190565b63ffffffff9290921682526001600160a01b0316602082015260400190565b6001600160401b0391909116815260200190565b6040518181016001600160401b03811182821017156158b457fe5b604052919050565b60006001600160401b038211156158cf57fe5b5060209081020190565b60005b838110156158f45781810151838201526020016158dc565b838111156137cb5750506000910152565b6001600160a01b038116811461078557600080fd5b6001600160801b038116811461078557600080fdfe8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4a26469706673582212205d24e64f92160aa27539d4ac0bb5a901363da1fb80c2c822b0b94be21629f83d64736f6c63430007060033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.