Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
To
|
Amount
|
||
|---|---|---|---|---|---|---|---|
| 0x60c06040 | 9180129 | 182 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Loading...
Loading
Contract Name:
MailboxFacet
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 9999999 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {Math} from "@openzeppelin/contracts-v4/utils/math/Math.sol";
import {IMailbox} from "../../chain-interfaces/IMailbox.sol";
import {IMailboxImpl} from "../../chain-interfaces/IMailboxImpl.sol";
import {IBridgehub} from "../../../bridgehub/IBridgehub.sol";
import {ITransactionFilterer} from "../../chain-interfaces/ITransactionFilterer.sol";
import {PriorityTree} from "../../libraries/PriorityTree.sol";
import {TransactionValidator} from "../../libraries/TransactionValidator.sol";
import {BridgehubL2TransactionRequest, L2CanonicalTransaction, L2Log, L2Message, TxStatus, WritePriorityOpParams} from "../../../common/Messaging.sol";
import {MessageHashing, ProofData} from "../../../common/libraries/MessageHashing.sol";
import {FeeParams, PubdataPricingMode} from "../ZKChainStorage.sol";
import {UncheckedMath} from "../../../common/libraries/UncheckedMath.sol";
import {L2ContractHelper} from "../../../common/l2-helpers/L2ContractHelper.sol";
import {AddressAliasHelper} from "../../../vendor/AddressAliasHelper.sol";
import {ZKChainBase} from "./ZKChainBase.sol";
import {L1_GAS_PER_PUBDATA_BYTE, MAX_NEW_FACTORY_DEPS, PRIORITY_EXPIRATION, PRIORITY_OPERATION_L2_TX_TYPE, REQUIRED_L2_GAS_PRICE_PER_PUBDATA, SERVICE_TRANSACTION_SENDER, SETTLEMENT_LAYER_RELAY_SENDER} from "../../../common/Config.sol";
import {L2_BOOTLOADER_ADDRESS, L2_BRIDGEHUB_ADDR} from "../../../common/l2-helpers/L2ContractAddresses.sol";
import {IL1AssetRouter} from "../../../bridge/asset-router/IL1AssetRouter.sol";
import {BaseTokenGasPriceDenominatorNotSet, BatchNotExecuted, GasPerPubdataMismatch, MsgValueTooLow, OnlyEraSupported, TooManyFactoryDeps, TransactionNotAllowed} from "../../../common/L1ContractErrors.sol";
import {InvalidChainId, LocalRootIsZero, LocalRootMustBeZero, NotHyperchain, NotL1, NotSettlementLayer} from "../../L1StateTransitionErrors.sol";
// While formally the following import is not used, it is needed to inherit documentation from it
import {IZKChainBase} from "../../chain-interfaces/IZKChainBase.sol";
import {MessageVerification, IMessageVerification} from "./MessageVerification.sol";
/// @title ZKsync Mailbox contract providing interfaces for L1 <-> L2 interaction.
/// @author Matter Labs
/// @custom:security-contact [email protected]
contract MailboxFacet is ZKChainBase, IMailboxImpl, MessageVerification {
using UncheckedMath for uint256;
using PriorityTree for PriorityTree.Tree;
/// @inheritdoc IZKChainBase
string public constant override getName = "MailboxFacet";
/// @dev Era's chainID
uint256 internal immutable ERA_CHAIN_ID;
/// @notice The chain id of L1. This contract can be deployed on multiple layers, but this value is still equal to the
/// L1 that is at the most base layer.
uint256 internal immutable L1_CHAIN_ID;
modifier onlyL1() {
if (block.chainid != L1_CHAIN_ID) {
revert NotL1(block.chainid);
}
_;
}
constructor(uint256 _eraChainId, uint256 _l1ChainId) {
ERA_CHAIN_ID = _eraChainId;
L1_CHAIN_ID = _l1ChainId;
}
/// @inheritdoc IMailboxImpl
function bridgehubRequestL2Transaction(
BridgehubL2TransactionRequest calldata _request
) external onlyBridgehub returns (bytes32 canonicalTxHash) {
canonicalTxHash = _requestL2TransactionSender(_request);
}
/// @inheritdoc IMessageVerification
function proveL2MessageInclusionShared(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _index,
L2Message calldata _message,
bytes32[] calldata _proof
) public view override returns (bool) {
if (s.chainId != _chainId) {
revert InvalidChainId();
}
return
super.proveL2MessageInclusionShared({
_chainId: _chainId,
_blockOrBatchNumber: _blockOrBatchNumber,
_index: _index,
_message: _message,
_proof: _proof
});
}
/// @inheritdoc IMailboxImpl
function proveL2MessageInclusion(
uint256 _batchNumber,
uint256 _index,
L2Message calldata _message,
bytes32[] calldata _proof
) public view returns (bool) {
return
_proveL2LogInclusion({
_chainId: s.chainId,
_blockOrBatchNumber: _batchNumber,
_index: _index,
_log: _l2MessageToLog(_message),
_proof: _proof
});
}
/// @inheritdoc IMessageVerification
function proveL2LogInclusionShared(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _index,
L2Log calldata _log,
bytes32[] calldata _proof
) public view override returns (bool) {
if (s.chainId != _chainId) {
revert InvalidChainId();
}
return
super.proveL2LogInclusionShared({
_chainId: _chainId,
_blockOrBatchNumber: _blockOrBatchNumber,
_index: _index,
_log: _log,
_proof: _proof
});
}
/// @inheritdoc IMailboxImpl
function proveL2LogInclusion(
uint256 _batchNumber,
uint256 _index,
L2Log calldata _log,
bytes32[] calldata _proof
) external view returns (bool) {
return
_proveL2LogInclusion({
_chainId: s.chainId,
_blockOrBatchNumber: _batchNumber,
_index: _index,
_log: _log,
_proof: _proof
});
}
/// @inheritdoc IMailboxImpl
function proveL1ToL2TransactionStatus(
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof,
TxStatus _status
) public view returns (bool) {
// Bootloader sends an L2 -> L1 log only after processing the L1 -> L2 transaction.
// Thus, we can verify that the L1 -> L2 transaction was included in the L2 batch with specified status.
//
// The semantics of such L2 -> L1 log is always:
// - sender = L2_BOOTLOADER_ADDRESS
// - key = hash(L1ToL2Transaction)
// - value = status of the processing transaction (1 - success & 0 - fail)
// - isService = true (just a conventional value)
// - l2ShardId = 0 (means that L1 -> L2 transaction was processed in a rollup shard, other shards are not available yet anyway)
// - txNumberInBatch = number of transaction in the batch
L2Log memory l2Log = L2Log({
l2ShardId: 0,
isService: true,
txNumberInBatch: _l2TxNumberInBatch,
sender: L2_BOOTLOADER_ADDRESS,
key: _l2TxHash,
value: bytes32(uint256(_status))
});
return
_proveL2LogInclusion({
_chainId: s.chainId,
_blockOrBatchNumber: _l2BatchNumber,
_index: _l2MessageIndex,
_log: l2Log,
_proof: _merkleProof
});
}
/// @inheritdoc IMessageVerification
function proveL2LeafInclusionShared(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _leafProofMask,
bytes32 _leaf,
bytes32[] calldata _proof
) public view virtual override returns (bool) {
if (s.chainId != _chainId) {
revert InvalidChainId();
}
return
super.proveL2LeafInclusionShared({
_chainId: _chainId,
_blockOrBatchNumber: _blockOrBatchNumber,
_leafProofMask: _leafProofMask,
_leaf: _leaf,
_proof: _proof
});
}
/// @inheritdoc IMailboxImpl
function proveL2LeafInclusion(
uint256 _batchNumber,
uint256 _leafProofMask,
bytes32 _leaf,
bytes32[] calldata _proof
) external view returns (bool) {
return
_proveL2LeafInclusion({
_chainId: s.chainId,
_batchNumber: _batchNumber,
_leafProofMask: _leafProofMask,
_leaf: _leaf,
_proof: _proof
});
}
function _proveL2LeafInclusion(
uint256 _chainId,
uint256 _batchNumber,
uint256 _leafProofMask,
bytes32 _leaf,
bytes32[] calldata _proof
) internal view override returns (bool) {
ProofData memory proofData = MessageHashing._getProofData({
_chainId: _chainId,
_batchNumber: _batchNumber,
_leafProofMask: _leafProofMask,
_leaf: _leaf,
_proof: _proof
});
// If the `finalProofNode` is true, then we assume that this is L1 contract of the top-level
// in the aggregation, i.e. the batch root is stored here on L1.
if (proofData.finalProofNode) {
// Double checking that the batch has been executed.
if (_batchNumber > s.totalBatchesExecuted) {
revert BatchNotExecuted(_batchNumber);
}
bytes32 correctBatchRoot = s.l2LogsRootHashes[_batchNumber];
if (correctBatchRoot == bytes32(0)) {
revert LocalRootIsZero();
}
return correctBatchRoot == proofData.batchSettlementRoot;
}
if (s.l2LogsRootHashes[_batchNumber] != bytes32(0)) {
revert LocalRootMustBeZero();
}
// Assuming that `settlementLayerChainId` is an honest chain, the `chainIdLeaf` should belong
// to a chain's message root only if the chain has indeed executed its batch on top of it.
//
// We trust all chains whitelisted by the Bridgehub governance.
if (!IBridgehub(s.bridgehub).whitelistedSettlementLayers(proofData.settlementLayerChainId)) {
revert NotSettlementLayer();
}
address settlementLayerAddress = IBridgehub(s.bridgehub).getZKChain(proofData.settlementLayerChainId);
return
IMailbox(settlementLayerAddress).proveL2LeafInclusion(
proofData.settlementLayerBatchNumber,
proofData.settlementLayerBatchRootMask,
proofData.chainIdLeaf,
MessageHashing.extractSliceUntilEnd(_proof, proofData.ptr)
);
}
/// @inheritdoc IMailboxImpl
function l2TransactionBaseCost(
uint256 _gasPrice,
uint256 _l2GasLimit,
uint256 _l2GasPerPubdataByteLimit
) public view returns (uint256) {
uint256 l2GasPrice = _deriveL2GasPrice(_gasPrice, _l2GasPerPubdataByteLimit);
return l2GasPrice * _l2GasLimit;
}
/// @notice Derives the price for L2 gas in base token to be paid.
/// @param _l1GasPrice The gas price on L1
/// @param _gasPerPubdata The price for each pubdata byte in L2 gas
/// @return The price of L2 gas in the base token
function _deriveL2GasPrice(uint256 _l1GasPrice, uint256 _gasPerPubdata) internal view returns (uint256) {
FeeParams memory feeParams = s.feeParams;
if (s.baseTokenGasPriceMultiplierDenominator == 0) {
revert BaseTokenGasPriceDenominatorNotSet();
}
uint256 l1GasPriceConverted = (_l1GasPrice * s.baseTokenGasPriceMultiplierNominator) /
s.baseTokenGasPriceMultiplierDenominator;
uint256 pubdataPriceBaseToken;
if (feeParams.pubdataPricingMode == PubdataPricingMode.Rollup) {
// slither-disable-next-line divide-before-multiply
pubdataPriceBaseToken = L1_GAS_PER_PUBDATA_BYTE * l1GasPriceConverted;
}
// slither-disable-next-line divide-before-multiply
uint256 batchOverheadBaseToken = uint256(feeParams.batchOverheadL1Gas) * l1GasPriceConverted;
uint256 fullPubdataPriceBaseToken = pubdataPriceBaseToken +
batchOverheadBaseToken /
uint256(feeParams.maxPubdataPerBatch);
uint256 l2GasPrice = feeParams.minimalL2GasPrice + batchOverheadBaseToken / uint256(feeParams.maxL2GasPerBatch);
uint256 minL2GasPriceBaseToken = (fullPubdataPriceBaseToken + _gasPerPubdata - 1) / _gasPerPubdata;
return Math.max(l2GasPrice, minL2GasPriceBaseToken);
}
/// @inheritdoc IMailboxImpl
function requestL2TransactionToGatewayMailbox(
uint256 _chainId,
bytes32 _canonicalTxHash,
uint64 _expirationTimestamp
) external override onlyL1 returns (bytes32 canonicalTxHash) {
if (!IBridgehub(s.bridgehub).whitelistedSettlementLayers(s.chainId)) {
revert NotSettlementLayer();
}
if (IBridgehub(s.bridgehub).getZKChain(_chainId) != msg.sender) {
revert NotHyperchain();
}
BridgehubL2TransactionRequest memory wrappedRequest = _wrapRequest({
_chainId: _chainId,
_canonicalTxHash: _canonicalTxHash,
_expirationTimestamp: _expirationTimestamp
});
canonicalTxHash = _requestL2TransactionFree(wrappedRequest);
}
/// @inheritdoc IMailboxImpl
function bridgehubRequestL2TransactionOnGateway(
bytes32 _canonicalTxHash,
uint64 _expirationTimestamp
) external override onlyBridgehub {
_writePriorityOpHash(_canonicalTxHash, _expirationTimestamp);
emit NewRelayedPriorityTransaction(_getTotalPriorityTxs(), _canonicalTxHash, _expirationTimestamp);
emit NewPriorityRequestId(_getTotalPriorityTxs(), _canonicalTxHash);
}
function _wrapRequest(
uint256 _chainId,
bytes32 _canonicalTxHash,
uint64 _expirationTimestamp
) internal view returns (BridgehubL2TransactionRequest memory) {
// solhint-disable-next-line func-named-parameters
bytes memory data = abi.encodeCall(
IBridgehub.forwardTransactionOnGateway,
(_chainId, _canonicalTxHash, _expirationTimestamp)
);
return
BridgehubL2TransactionRequest({
/// There is no sender for the wrapping, we use a virtual address.
sender: SETTLEMENT_LAYER_RELAY_SENDER,
contractL2: L2_BRIDGEHUB_ADDR,
mintValue: 0,
l2Value: 0,
// Very large amount
l2GasLimit: 72_000_000,
l2Calldata: data,
l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA,
factoryDeps: new bytes[](0),
// Tx is free, no so refund recipient needed
refundRecipient: address(0)
});
}
/// @inheritdoc IMailboxImpl
function requestL2ServiceTransaction(
address _contractL2,
bytes calldata _l2Calldata
) external onlySelf returns (bytes32 canonicalTxHash) {
canonicalTxHash = _requestL2TransactionFree(
BridgehubL2TransactionRequest({
sender: SERVICE_TRANSACTION_SENDER,
contractL2: _contractL2,
mintValue: 0,
l2Value: 0,
// Very large amount
l2GasLimit: 72_000_000,
l2Calldata: _l2Calldata,
l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA,
factoryDeps: new bytes[](0),
// Tx is free, so no refund recipient needed
refundRecipient: address(0)
})
);
if (s.settlementLayer != address(0)) {
// slither-disable-next-line unused-return
IMailbox(s.settlementLayer).requestL2TransactionToGatewayMailbox({
_chainId: s.chainId,
_canonicalTxHash: canonicalTxHash,
_expirationTimestamp: uint64(block.timestamp + PRIORITY_EXPIRATION)
});
}
}
function _requestL2TransactionSender(
BridgehubL2TransactionRequest memory _request
) internal nonReentrant returns (bytes32 canonicalTxHash) {
// Check that the transaction is allowed by the filterer (if the filterer is set).
if (s.transactionFilterer != address(0)) {
if (
!ITransactionFilterer(s.transactionFilterer).isTransactionAllowed({
sender: _request.sender,
contractL2: _request.contractL2,
mintValue: _request.mintValue,
l2Value: _request.l2Value,
l2Calldata: _request.l2Calldata,
refundRecipient: _request.refundRecipient
})
) {
revert TransactionNotAllowed();
}
}
// Enforcing that `_request.l2GasPerPubdataByteLimit` equals to a certain constant number. This is needed
// to ensure that users do not get used to using "exotic" numbers for _request.l2GasPerPubdataByteLimit, e.g. 1-2, etc.
// VERY IMPORTANT: nobody should rely on this constant to be fixed and every contract should give their users the ability to provide the
// ability to provide `_request.l2GasPerPubdataByteLimit` for each independent transaction.
// CHANGING THIS CONSTANT SHOULD BE A CLIENT-SIDE CHANGE.
if (_request.l2GasPerPubdataByteLimit != REQUIRED_L2_GAS_PRICE_PER_PUBDATA) {
revert GasPerPubdataMismatch();
}
WritePriorityOpParams memory params;
params.request = _request;
canonicalTxHash = _requestL2Transaction(params);
}
function _requestL2Transaction(WritePriorityOpParams memory _params) internal returns (bytes32 canonicalTxHash) {
BridgehubL2TransactionRequest memory request = _params.request;
if (request.factoryDeps.length > MAX_NEW_FACTORY_DEPS) {
revert TooManyFactoryDeps();
}
_params.txId = _nextPriorityTxId();
// Checking that the user provided enough ether to pay for the transaction.
_params.l2GasPrice = _deriveL2GasPrice(tx.gasprice, request.l2GasPerPubdataByteLimit);
uint256 baseCost = _params.l2GasPrice * request.l2GasLimit;
if (request.mintValue < baseCost + request.l2Value) {
revert MsgValueTooLow(baseCost + request.l2Value, request.mintValue);
}
request.refundRecipient = AddressAliasHelper.actualRefundRecipient(request.refundRecipient, request.sender);
// Change the sender address if it is a smart contract to prevent address collision between L1 and L2.
// Please note, currently ZKsync address derivation is different from Ethereum one, but it may be changed in the future.
// solhint-disable avoid-tx-origin
// slither-disable-next-line tx-origin
if (request.sender != tx.origin) {
request.sender = AddressAliasHelper.applyL1ToL2Alias(request.sender);
}
// populate missing fields
_params.expirationTimestamp = uint64(block.timestamp + PRIORITY_EXPIRATION); // Safe to cast
L2CanonicalTransaction memory transaction;
(transaction, canonicalTxHash) = _validateTx(_params);
_writePriorityOp(transaction, _params.request.factoryDeps, canonicalTxHash, _params.expirationTimestamp);
if (s.settlementLayer != address(0)) {
// slither-disable-next-line unused-return
IMailbox(s.settlementLayer).requestL2TransactionToGatewayMailbox({
_chainId: s.chainId,
_canonicalTxHash: canonicalTxHash,
_expirationTimestamp: _params.expirationTimestamp
});
}
}
function _nextPriorityTxId() internal view returns (uint256) {
return s.priorityTree.getTotalPriorityTxs();
}
function _requestL2TransactionFree(
BridgehubL2TransactionRequest memory _request
) internal nonReentrant returns (bytes32 canonicalTxHash) {
WritePriorityOpParams memory params = WritePriorityOpParams({
request: _request,
txId: _nextPriorityTxId(),
l2GasPrice: 0,
expirationTimestamp: uint64(block.timestamp + PRIORITY_EXPIRATION)
});
L2CanonicalTransaction memory transaction;
(transaction, canonicalTxHash) = _validateTx(params);
_writePriorityOp(transaction, params.request.factoryDeps, canonicalTxHash, params.expirationTimestamp);
}
function _serializeL2Transaction(
WritePriorityOpParams memory _priorityOpParams
) internal pure returns (L2CanonicalTransaction memory transaction) {
BridgehubL2TransactionRequest memory request = _priorityOpParams.request;
transaction = L2CanonicalTransaction({
txType: PRIORITY_OPERATION_L2_TX_TYPE,
from: uint256(uint160(request.sender)),
to: uint256(uint160(request.contractL2)),
gasLimit: request.l2GasLimit,
gasPerPubdataByteLimit: request.l2GasPerPubdataByteLimit,
maxFeePerGas: uint256(_priorityOpParams.l2GasPrice),
maxPriorityFeePerGas: uint256(0),
paymaster: uint256(0),
// Note, that the priority operation id is used as "nonce" for L1->L2 transactions
nonce: uint256(_priorityOpParams.txId),
value: request.l2Value,
reserved: [request.mintValue, uint256(uint160(request.refundRecipient)), 0, 0],
data: request.l2Calldata,
signature: new bytes(0),
factoryDeps: L2ContractHelper.hashFactoryDeps(request.factoryDeps),
paymasterInput: new bytes(0),
reservedDynamic: new bytes(0)
});
}
function _validateTx(
WritePriorityOpParams memory _priorityOpParams
) internal view returns (L2CanonicalTransaction memory transaction, bytes32 canonicalTxHash) {
transaction = _serializeL2Transaction(_priorityOpParams);
bytes memory transactionEncoding = abi.encode(transaction);
TransactionValidator.validateL1ToL2Transaction(
transaction,
transactionEncoding,
s.priorityTxMaxGasLimit,
s.feeParams.priorityTxMaxPubdata
);
canonicalTxHash = keccak256(transactionEncoding);
}
/// @notice Stores a transaction record in storage & send event about that
function _writePriorityOp(
L2CanonicalTransaction memory _transaction,
bytes[] memory _factoryDeps,
bytes32 _canonicalTxHash,
uint64 _expirationTimestamp
) internal {
_writePriorityOpHash(_canonicalTxHash, _expirationTimestamp);
// Data that is needed for the operator to simulate priority queue offchain
// solhint-disable-next-line func-named-parameters
emit NewPriorityRequest(_transaction.nonce, _canonicalTxHash, _expirationTimestamp, _transaction, _factoryDeps);
emit NewPriorityRequestId(_transaction.nonce, _canonicalTxHash);
}
// solhint-disable-next-line no-unused-vars
function _writePriorityOpHash(bytes32 _canonicalTxHash, uint64 _expirationTimestamp) internal {
s.priorityTree.push(_canonicalTxHash);
}
///////////////////////////////////////////////////////
//////// Legacy Era functions
/// @inheritdoc IMailboxImpl
function finalizeEthWithdrawal(
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes calldata _message,
bytes32[] calldata _merkleProof
) external nonReentrant onlyL1 {
if (s.chainId != ERA_CHAIN_ID) {
revert OnlyEraSupported();
}
address sharedBridge = IBridgehub(s.bridgehub).assetRouter();
IL1AssetRouter(sharedBridge).finalizeWithdrawal({
_chainId: ERA_CHAIN_ID,
_l2BatchNumber: _l2BatchNumber,
_l2MessageIndex: _l2MessageIndex,
_l2TxNumberInBatch: _l2TxNumberInBatch,
_message: _message,
_merkleProof: _merkleProof
});
}
/// @inheritdoc IMailboxImpl
function requestL2Transaction(
address _contractL2,
uint256 _l2Value,
bytes calldata _calldata,
uint256 _l2GasLimit,
uint256 _l2GasPerPubdataByteLimit,
bytes[] calldata _factoryDeps,
address _refundRecipient
) external payable onlyL1 returns (bytes32 canonicalTxHash) {
if (s.chainId != ERA_CHAIN_ID) {
revert OnlyEraSupported();
}
canonicalTxHash = _requestL2TransactionSender(
BridgehubL2TransactionRequest({
sender: msg.sender,
contractL2: _contractL2,
mintValue: msg.value,
l2Value: _l2Value,
l2GasLimit: _l2GasLimit,
l2Calldata: _calldata,
l2GasPerPubdataByteLimit: _l2GasPerPubdataByteLimit,
factoryDeps: _factoryDeps,
refundRecipient: _refundRecipient
})
);
address sharedBridge = IBridgehub(s.bridgehub).assetRouter();
IL1AssetRouter(sharedBridge).bridgehubDepositBaseToken{value: msg.value}(
s.chainId,
s.baseTokenAssetId,
msg.sender,
msg.value
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {IMessageVerification} from "./IMessageVerification.sol";
import {IMailboxImpl} from "./IMailboxImpl.sol";
/// @title The interface of the ZKsync Mailbox contract that provides interfaces for L1 <-> L2 interaction.
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IMailbox is IMessageVerification, IMailboxImpl {}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {IZKChainBase} from "./IZKChainBase.sol";
import {BridgehubL2TransactionRequest, L2CanonicalTransaction, L2Log, L2Message, TxStatus} from "../../common/Messaging.sol";
/// @title The interface of the ZKsync Mailbox contract that provides functions for L1 <-> L2 interaction.
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IMailboxImpl is IZKChainBase {
/// @notice Prove that a specific arbitrary-length message was sent in a specific L2 batch number.
/// @param _batchNumber The executed L2 batch number in which the message appeared.
/// @param _index The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
/// @param _message Information about the sent message: sender address, the message itself, tx index in the L2 batch where the message was sent.
/// @param _proof Merkle proof for inclusion of L2 log that was sent with the message.
/// @return Boolean specifying whether the proof is valid.
function proveL2MessageInclusion(
uint256 _batchNumber,
uint256 _index,
L2Message calldata _message,
bytes32[] calldata _proof
) external view returns (bool);
/// @notice Prove that a specific L2 log was sent in a specific L2 batch.
/// @param _batchNumber The executed L2 batch number in which the log appeared.
/// @param _index The position of the l2log in the L2 logs Merkle tree.
/// @param _log Information about the sent log.
/// @param _proof Merkle proof for inclusion of the L2 log.
/// @return Whether the proof is correct and L2 log is included in batch.
function proveL2LogInclusion(
uint256 _batchNumber,
uint256 _index,
L2Log calldata _log,
bytes32[] calldata _proof
) external view returns (bool);
/// @notice Prove that the L1 -> L2 transaction was processed with the specified status.
/// @param _l2TxHash The L2 canonical transaction hash.
/// @param _l2BatchNumber The L2 batch number where the transaction was processed.
/// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
/// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent.
/// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction.
/// @param _status The execution status of the L1 -> L2 transaction (true - success & 0 - fail).
/// @return Whether the proof is correct and the transaction was actually executed with provided status.
/// NOTE: It may return `false` for incorrect proof, but it doesn't mean that the L1 -> L2 transaction has an opposite status!
function proveL1ToL2TransactionStatus(
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof,
TxStatus _status
) external view returns (bool);
/// @notice Finalize the withdrawal and release funds.
/// @param _l2BatchNumber The L2 batch number where the withdrawal was processed.
/// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
/// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent.
/// @param _message The L2 withdraw data, stored in an L2 -> L1 message.
/// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization.
function finalizeEthWithdrawal(
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes calldata _message,
bytes32[] calldata _merkleProof
) external;
/// @notice Request execution of L2 transaction from L1.
/// @param _contractL2 The L2 receiver address.
/// @param _l2Value `msg.value` of L2 transaction.
/// @param _calldata The input of the L2 transaction.
/// @param _l2GasLimit Maximum amount of L2 gas that transaction can consume during execution on L2.
/// @param _l2GasPerPubdataByteLimit The maximum amount L2 gas that the operator may charge the user for single byte of pubdata.
/// @param _factoryDeps An array of L2 bytecodes that will be marked as known on L2.
/// @param _refundRecipient The address on L2 that will receive the refund for the transaction.
/// @dev If the L2 deposit finalization transaction fails, the `_refundRecipient` will receive the `_l2Value`.
/// Please note, the contract may change the refund recipient's address to eliminate sending funds to addresses out of control.
/// - If `_refundRecipient` is a contract on L1, the refund will be sent to the aliased `_refundRecipient`.
/// - If `_refundRecipient` is set to `address(0)` and the sender has NO deployed bytecode on L1, the refund will be sent to the `msg.sender` address.
/// - If `_refundRecipient` is set to `address(0)` and the sender has deployed bytecode on L1, the refund will be sent to the aliased `msg.sender` address.
/// @dev The address aliasing of L1 contracts as refund recipient on L2 is necessary to guarantee that the funds are controllable,
/// since address aliasing to the from address for the L2 tx will be applied if the L1 `msg.sender` is a contract.
/// Without address aliasing for L1 contracts as refund recipients they would not be able to make proper L2 tx requests
/// through the Mailbox to use or withdraw the funds from L2, and the funds would be lost.
/// @return canonicalTxHash The hash of the requested L2 transaction. This hash can be used to follow the transaction status.
function requestL2Transaction(
address _contractL2,
uint256 _l2Value,
bytes calldata _calldata,
uint256 _l2GasLimit,
uint256 _l2GasPerPubdataByteLimit,
bytes[] calldata _factoryDeps,
address _refundRecipient
) external payable returns (bytes32 canonicalTxHash);
/// @notice Request execution of L2 transaction through the Bridgehub.
/// @dev Only accessible from L1, this is getting checked in the Bridgehub.
/// @param _request the request for the L2 transaction.
function bridgehubRequestL2Transaction(
BridgehubL2TransactionRequest calldata _request
) external returns (bytes32 canonicalTxHash);
/// @notice The chain's mailbox receives the tx from the Bridgehub on Gateway.
/// @param _canonicalTxHash the canonical transaction hash.
/// @param _expirationTimestamp the expiration timestamp for the transaction.
function bridgehubRequestL2TransactionOnGateway(bytes32 _canonicalTxHash, uint64 _expirationTimestamp) external;
/// @notice Request execution of service L2 transaction from L1.
/// @dev Used for chain configuration. Can be called only by DiamondProxy itself.
/// @param _contractL2 The L2 receiver address.
/// @param _l2Calldata The input of the L2 transaction.
function requestL2ServiceTransaction(
address _contractL2,
bytes calldata _l2Calldata
) external returns (bytes32 canonicalTxHash);
/// @dev On L1 we have to forward to the Gateway's mailbox which sends to the Bridgehub on the Gateway.
/// @param _chainId the chainId of the chain.
/// @param _canonicalTxHash the canonical transaction hash.
/// @param _expirationTimestamp the expiration timestamp.
function requestL2TransactionToGatewayMailbox(
uint256 _chainId,
bytes32 _canonicalTxHash,
uint64 _expirationTimestamp
) external returns (bytes32 canonicalTxHash);
/// @notice Estimates the cost in Ether of requesting execution of an L2 transaction from L1.
/// @param _gasPrice expected L1 gas price at which the user requests the transaction execution.
/// @param _l2GasLimit Maximum amount of L2 gas that transaction can consume during execution on L2.
/// @param _l2GasPerPubdataByteLimit The maximum amount of L2 gas that the operator may charge the user for a single byte of pubdata.
/// @return The estimated ETH spent on L2 gas for the transaction.
function l2TransactionBaseCost(
uint256 _gasPrice,
uint256 _l2GasLimit,
uint256 _l2GasPerPubdataByteLimit
) external view returns (uint256);
/// @dev Proves that a certain leaf was included as part of the log merkle tree.
/// @dev Warning: this function does not enforce any additional checks on the structure
/// of the leaf. This means that it can accept intermediate nodes of the Merkle tree as a `_leaf` as
/// well as the default "empty" leaves. It is the responsibility of the caller to ensure that the
/// `_leaf` is a hash of a valid leaf.
/// @param _batchNumber The batch number of the leaf to be proven.
/// @param _leafProofMask The leaf proof mask.
/// @param _leaf The leaf to be proven.
/// @param _proof The proof.
function proveL2LeafInclusion(
uint256 _batchNumber,
uint256 _leafProofMask,
bytes32 _leaf,
bytes32[] calldata _proof
) external view returns (bool);
/// @notice New priority request event. Emitted when a request is placed into the priority queue.
/// @param txId Serial number of the priority operation.
/// @param txHash keccak256 hash of encoded transaction representation.
/// @param expirationTimestamp Timestamp up to which priority request should be processed.
/// @param transaction The whole transaction structure that is requested to be executed on L2.
/// @param factoryDeps An array of bytecodes that were shown in the L1 public data.
/// Will be marked as known bytecodes in L2.
event NewPriorityRequest(
uint256 txId,
bytes32 txHash,
uint64 expirationTimestamp,
L2CanonicalTransaction transaction,
bytes[] factoryDeps
);
/// @notice Indexed new priority request event. Emitted when a request is placed into the priority queue.
/// @dev We define a new event similar to NewPriorityRequest, as modifying it could break existing indexers.
/// The indexed txId and txHash helps to simplify external node implementation for fast finality.
/// @param txId Serial number of the priority operation.
/// @param txHash keccak256 hash of encoded transaction representation.
event NewPriorityRequestId(uint256 indexed txId, bytes32 indexed txHash);
/// @notice New relayed priority request event. It is emitted on a chain that is deployed
/// on top of the gateway when it receives a request relayed via the Bridgehub.
/// @dev IMPORTANT: this event most likely will be removed in the future, so
/// no one should rely on it for indexing purposes.
/// @param txId Serial number of the priority operation.
/// @param txHash keccak256 hash of encoded transaction representation.
/// @param expirationTimestamp Timestamp up to which priority request should be processed.
event NewRelayedPriorityTransaction(uint256 txId, bytes32 txHash, uint64 expirationTimestamp);
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {L2Log, L2Message, TxStatus} from "../common/Messaging.sol";
import {ICTMDeploymentTracker} from "./ICTMDeploymentTracker.sol";
import {IMessageRoot} from "./IMessageRoot.sol";
struct L2TransactionRequestDirect {
uint256 chainId;
uint256 mintValue;
address l2Contract;
uint256 l2Value;
bytes l2Calldata;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
bytes[] factoryDeps;
address refundRecipient;
}
struct L2TransactionRequestTwoBridgesOuter {
uint256 chainId;
uint256 mintValue;
uint256 l2Value;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
address refundRecipient;
address secondBridgeAddress;
uint256 secondBridgeValue;
bytes secondBridgeCalldata;
}
struct L2TransactionRequestTwoBridgesInner {
bytes32 magicValue;
address l2Contract;
bytes l2Calldata;
bytes[] factoryDeps;
bytes32 txDataHash;
}
struct BridgehubMintCTMAssetData {
uint256 chainId;
bytes32 baseTokenAssetId;
bytes ctmData;
bytes chainData;
}
struct BridgehubBurnCTMAssetData {
uint256 chainId;
bytes ctmData;
bytes chainData;
}
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IBridgehub {
/// @notice pendingAdmin is changed
/// @dev Also emitted when new admin is accepted and in this case, `newPendingAdmin` would be zero address
event NewPendingAdmin(address indexed oldPendingAdmin, address indexed newPendingAdmin);
/// @notice Admin changed
event NewAdmin(address indexed oldAdmin, address indexed newAdmin);
/// @notice CTM asset registered
event AssetRegistered(
bytes32 indexed assetInfo,
address indexed _assetAddress,
bytes32 indexed additionalData,
address sender
);
event SettlementLayerRegistered(uint256 indexed chainId, bool indexed isWhitelisted);
/// @notice Starts the transfer of admin rights. Only the current admin or owner can propose a new pending one.
/// @notice New admin can accept admin rights by calling `acceptAdmin` function.
/// @param _newPendingAdmin Address of the new admin
function setPendingAdmin(address _newPendingAdmin) external;
/// @notice Accepts transfer of admin rights. Only pending admin can accept the role.
function acceptAdmin() external;
/// Getters
function chainTypeManagerIsRegistered(address _chainTypeManager) external view returns (bool);
function chainTypeManager(uint256 _chainId) external view returns (address);
function assetIdIsRegistered(bytes32 _baseTokenAssetId) external view returns (bool);
function baseToken(uint256 _chainId) external view returns (address);
function baseTokenAssetId(uint256 _chainId) external view returns (bytes32);
function messageRoot() external view returns (IMessageRoot);
function getZKChain(uint256 _chainId) external view returns (address);
function getAllZKChains() external view returns (address[] memory);
function getAllZKChainChainIDs() external view returns (uint256[] memory);
function migrationPaused() external view returns (bool);
function admin() external view returns (address);
function assetRouter() external view returns (address);
/// Mailbox forwarder
function proveL2MessageInclusion(
uint256 _chainId,
uint256 _batchNumber,
uint256 _index,
L2Message calldata _message,
bytes32[] calldata _proof
) external view returns (bool);
function proveL2LogInclusion(
uint256 _chainId,
uint256 _batchNumber,
uint256 _index,
L2Log memory _log,
bytes32[] calldata _proof
) external view returns (bool);
function proveL1ToL2TransactionStatus(
uint256 _chainId,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof,
TxStatus _status
) external view returns (bool);
function requestL2TransactionDirect(
L2TransactionRequestDirect calldata _request
) external payable returns (bytes32 canonicalTxHash);
function requestL2TransactionTwoBridges(
L2TransactionRequestTwoBridgesOuter calldata _request
) external payable returns (bytes32 canonicalTxHash);
function l2TransactionBaseCost(
uint256 _chainId,
uint256 _gasPrice,
uint256 _l2GasLimit,
uint256 _l2GasPerPubdataByteLimit
) external view returns (uint256);
//// Registry
function createNewChain(
uint256 _chainId,
address _chainTypeManager,
bytes32 _baseTokenAssetId,
uint256 _salt,
address _admin,
bytes calldata _initData,
bytes[] calldata _factoryDeps
) external returns (uint256 chainId);
function addChainTypeManager(address _chainTypeManager) external;
function removeChainTypeManager(address _chainTypeManager) external;
function addTokenAssetId(bytes32 _baseTokenAssetId) external;
function setAddresses(
address _sharedBridge,
ICTMDeploymentTracker _l1CtmDeployer,
IMessageRoot _messageRoot,
address _chainAssetHandler
) external;
function setChainAssetHandler(address _chainAssetHandler) external;
event NewChain(uint256 indexed chainId, address chainTypeManager, address indexed chainGovernance);
event ChainTypeManagerAdded(address indexed chainTypeManager);
event ChainTypeManagerRemoved(address indexed chainTypeManager);
event BaseTokenAssetIdRegistered(bytes32 indexed assetId);
function whitelistedSettlementLayers(uint256 _chainId) external view returns (bool);
function registerSettlementLayer(uint256 _newSettlementLayerChainId, bool _isWhitelisted) external;
function settlementLayer(uint256 _chainId) external view returns (uint256);
// function finalizeMigrationToGateway(
// uint256 _chainId,
// address _baseToken,
// address _sharedBridge,
// address _admin,
// uint256 _expectedProtocolVersion,
// ZKChainCommitment calldata _commitment,
// bytes calldata _diamondCut
// ) external;
function forwardTransactionOnGateway(
uint256 _chainId,
bytes32 _canonicalTxHash,
uint64 _expirationTimestamp
) external;
function ctmAssetIdFromChainId(uint256 _chainId) external view returns (bytes32);
function ctmAssetIdFromAddress(address _ctmAddress) external view returns (bytes32);
function l1CtmDeployer() external view returns (ICTMDeploymentTracker);
function ctmAssetIdToAddress(bytes32 _assetInfo) external view returns (address);
function setCTMAssetAddress(bytes32 _additionalData, address _assetAddress) external;
function L1_CHAIN_ID() external view returns (uint256);
function chainAssetHandler() external view returns (address);
function registerAlreadyDeployedZKChain(uint256 _chainId, address _hyperchain) external;
function registerLegacyChain(uint256 _chainId) external;
function pauseMigration() external;
function unpauseMigration() external;
function forwardedBridgeBurnSetSettlementLayer(
uint256 _chainId,
uint256 _newSettlementLayerChainId
) external returns (address zkChain, address ctm);
function forwardedBridgeMint(
bytes32 _assetId,
uint256 _chainId,
bytes32 _baseTokenAssetId
) external returns (address zkChain, address ctm);
function registerNewZKChain(uint256 _chainId, address _zkChain, bool _checkMaxNumberOfZKChains) external;
function forwardedBridgeRecoverFailedTransfer(uint256 _chainId) external returns (address zkChain, address ctm);
}// SPDX-License-Identifier: MIT // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version. pragma solidity ^0.8.21; /// @title The interface of the L1 -> L2 transaction filterer. /// @author Matter Labs /// @custom:security-contact [email protected] interface ITransactionFilterer { /// @notice Check if the transaction is allowed /// @param sender The sender of the transaction /// @param contractL2 The L2 receiver address /// @param mintValue The value of the L1 transaction /// @param l2Value The msg.value of the L2 transaction /// @param l2Calldata The calldata of the L2 transaction /// @param refundRecipient The address to refund the excess value /// @return Whether the transaction is allowed function isTransactionAllowed( address sender, address contractL2, uint256 mintValue, uint256 l2Value, bytes memory l2Calldata, address refundRecipient ) external view returns (bool); }
// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the zkSync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {DynamicIncrementalMerkle} from "../../common/libraries/DynamicIncrementalMerkle.sol";
import {Merkle} from "../../common/libraries/Merkle.sol";
import {PriorityTreeCommitment} from "../../common/Config.sol";
import {InvalidCommitment, InvalidNextLeafIndex, InvalidStartIndex, InvalidUnprocessedIndex, NotHistoricalRoot} from "../L1StateTransitionErrors.sol";
struct PriorityOpsBatchInfo {
bytes32[] leftPath;
bytes32[] rightPath;
bytes32[] itemHashes;
}
bytes32 constant ZERO_LEAF_HASH = keccak256("");
library PriorityTree {
using PriorityTree for Tree;
using DynamicIncrementalMerkle for DynamicIncrementalMerkle.Bytes32PushTree;
struct Tree {
uint256 startIndex; // priority tree started accepting priority ops from this index
uint256 unprocessedIndex; // relative to `startIndex`
mapping(bytes32 => bool) historicalRoots;
DynamicIncrementalMerkle.Bytes32PushTree tree;
}
/// @notice Returns zero if and only if no operations were processed from the tree
/// @return Index of the oldest priority operation that wasn't processed yet
function getFirstUnprocessedPriorityTx(Tree storage _tree) internal view returns (uint256) {
return _tree.startIndex + _tree.unprocessedIndex;
}
/// @return The total number of priority operations that were added to the priority queue, including all processed ones
function getTotalPriorityTxs(Tree storage _tree) internal view returns (uint256) {
return _tree.startIndex + _tree.tree._nextLeafIndex;
}
/// @return The total number of unprocessed priority operations in a priority queue
function getSize(Tree storage _tree) internal view returns (uint256) {
return _tree.tree._nextLeafIndex - _tree.unprocessedIndex;
}
/// @notice Add the priority operation to the end of the priority queue
function push(Tree storage _tree, bytes32 _hash) internal {
(, bytes32 newRoot) = _tree.tree.push(_hash);
_tree.historicalRoots[newRoot] = true;
}
/// @notice Set up the tree
function setup(Tree storage _tree, uint256 _startIndex) internal {
bytes32 initialRoot = _tree.tree.setup(ZERO_LEAF_HASH);
_tree.historicalRoots[initialRoot] = true;
_tree.startIndex = _startIndex;
}
/// @return Returns the tree root.
function getRoot(Tree storage _tree) internal view returns (bytes32) {
return _tree.tree.root();
}
/// @param _root The root to check.
/// @return Returns true if the root is a historical root.
function isHistoricalRoot(Tree storage _tree, bytes32 _root) internal view returns (bool) {
return _tree.historicalRoots[_root];
}
/// @notice Process the priority operations of a batch.
/// @dev Note, that the function below only checks that a certain segment of items is present in the tree.
/// It does not check that e.g. there are no zero items inside the provided `itemHashes`, so in theory proofs
/// that include non-existing priority operations could be created. This function relies on the fact
/// that the `itemHashes` of `_priorityOpsData` are hashes of valid priority transactions.
/// This fact is ensured by the fact the rolling hash of those is sent to the Executor by the bootloader
/// and so assuming that zero knowledge proofs are correct, so is the structure of the `itemHashes`.
function processBatch(Tree storage _tree, PriorityOpsBatchInfo memory _priorityOpsData) internal {
if (_priorityOpsData.itemHashes.length > 0) {
bytes32 expectedRoot = Merkle.calculateRootPaths(
_priorityOpsData.leftPath,
_priorityOpsData.rightPath,
_tree.unprocessedIndex,
_priorityOpsData.itemHashes
);
if (!_tree.historicalRoots[expectedRoot]) {
revert NotHistoricalRoot(expectedRoot);
}
_tree.unprocessedIndex += _priorityOpsData.itemHashes.length;
}
}
/// @notice Allows to skip a certain number of operations.
/// @param _lastUnprocessed The new expected id of the unprocessed transaction.
/// @dev It is used when the corresponding transactions have been processed by priority queue.
function skipUntil(Tree storage _tree, uint256 _lastUnprocessed) internal {
if (_tree.startIndex > _lastUnprocessed) {
// Nothing to do, return
return;
}
uint256 newUnprocessedIndex = _lastUnprocessed - _tree.startIndex;
if (newUnprocessedIndex <= _tree.unprocessedIndex) {
// These transactions were already processed, skip.
return;
}
_tree.unprocessedIndex = newUnprocessedIndex;
}
/// @notice Initialize a chain from a commitment.
function initFromCommitment(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal {
uint256 height = _commitment.sides.length; // Height, including the root node.
if (height == 0) {
revert InvalidCommitment();
}
_tree.startIndex = _commitment.startIndex;
_tree.unprocessedIndex = _commitment.unprocessedIndex;
_tree.tree._nextLeafIndex = _commitment.nextLeafIndex;
_tree.tree._sides = _commitment.sides;
bytes32 zero = ZERO_LEAF_HASH;
_tree.tree._zeros = new bytes32[](height);
for (uint256 i; i < height; ++i) {
_tree.tree._zeros[i] = zero;
zero = Merkle.efficientHash(zero, zero);
}
_tree.historicalRoots[_tree.tree.root()] = true;
}
/// @notice Reinitialize the tree from a commitment on L1.
function l1Reinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal {
if (_tree.startIndex != _commitment.startIndex) {
revert InvalidStartIndex(_tree.startIndex, _commitment.startIndex);
}
if (_tree.unprocessedIndex > _commitment.unprocessedIndex) {
revert InvalidUnprocessedIndex(_tree.unprocessedIndex, _commitment.unprocessedIndex);
}
if (_tree.tree._nextLeafIndex < _commitment.nextLeafIndex) {
revert InvalidNextLeafIndex(_tree.tree._nextLeafIndex, _commitment.nextLeafIndex);
}
_tree.unprocessedIndex = _commitment.unprocessedIndex;
}
/// @notice Reinitialize the tree from a commitment on GW.
function checkGWReinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal view {
if (_tree.startIndex != _commitment.startIndex) {
revert InvalidStartIndex(_tree.startIndex, _commitment.startIndex);
}
if (_tree.unprocessedIndex > _commitment.unprocessedIndex) {
revert InvalidUnprocessedIndex(_tree.unprocessedIndex, _commitment.unprocessedIndex);
}
if (_tree.tree._nextLeafIndex > _commitment.nextLeafIndex) {
revert InvalidNextLeafIndex(_tree.tree._nextLeafIndex, _commitment.nextLeafIndex);
}
}
/// @notice Returns the commitment to the priority tree.
function getCommitment(Tree storage _tree) internal view returns (PriorityTreeCommitment memory commitment) {
commitment.nextLeafIndex = _tree.tree._nextLeafIndex;
commitment.startIndex = _tree.startIndex;
commitment.unprocessedIndex = _tree.unprocessedIndex;
commitment.sides = _tree.tree._sides;
}
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {Math} from "@openzeppelin/contracts-v4/utils/math/Math.sol";
import {L2CanonicalTransaction} from "../../common/Messaging.sol";
import {L1_TX_DELTA_544_ENCODING_BYTES, L1_TX_DELTA_FACTORY_DEPS_L2_GAS, L1_TX_DELTA_FACTORY_DEPS_PUBDATA, L1_TX_INTRINSIC_L2_GAS, L1_TX_INTRINSIC_PUBDATA, L1_TX_MIN_L2_GAS_BASE, MEMORY_OVERHEAD_GAS, TX_SLOT_OVERHEAD_L2_GAS} from "../../common/Config.sol";
import {InvalidUpgradeTxn, PubdataGreaterThanLimit, TooMuchGas, TxnBodyGasLimitNotEnoughGas, UpgradeTxVerifyParam, ValidateTxnNotEnoughGas} from "../../common/L1ContractErrors.sol";
/// @title ZKsync Library for validating L1 -> L2 transactions
/// @author Matter Labs
/// @custom:security-contact [email protected]
library TransactionValidator {
/// @dev Used to validate key properties of an L1->L2 transaction
/// @param _transaction The transaction to validate
/// @param _encoded The abi encoded bytes of the transaction
/// @param _priorityTxMaxGasLimit The max gas limit, generally provided from Storage.sol
/// @param _priorityTxMaxPubdata The maximal amount of pubdata that a single L1->L2 transaction can emit
function validateL1ToL2Transaction(
L2CanonicalTransaction memory _transaction,
bytes memory _encoded,
uint256 _priorityTxMaxGasLimit,
uint256 _priorityTxMaxPubdata
) internal pure {
uint256 l2GasForTxBody = getTransactionBodyGasLimit(_transaction.gasLimit, _encoded.length);
// Ensuring that the transaction is provable
if (l2GasForTxBody > _priorityTxMaxGasLimit) {
revert TooMuchGas();
}
// Ensuring that the transaction cannot output more pubdata than is processable
if (l2GasForTxBody / _transaction.gasPerPubdataByteLimit > _priorityTxMaxPubdata) {
revert PubdataGreaterThanLimit(_priorityTxMaxPubdata, l2GasForTxBody / _transaction.gasPerPubdataByteLimit);
}
// Ensuring that the transaction covers the minimal costs for its processing:
// hashing its content, publishing the factory dependencies, etc.
if (
getMinimalPriorityTransactionGasLimit(
_encoded.length,
_transaction.factoryDeps.length,
_transaction.gasPerPubdataByteLimit
) > l2GasForTxBody
) {
revert ValidateTxnNotEnoughGas();
}
}
/// @dev Used to validate upgrade transactions
/// @param _transaction The transaction to validate
function validateUpgradeTransaction(L2CanonicalTransaction memory _transaction) internal pure {
// Restrict from to be within system contract range (0...2^16 - 1)
if (_transaction.from > type(uint16).max) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.From);
}
if (_transaction.to > type(uint160).max) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.To);
}
if (_transaction.paymaster != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Paymaster);
}
if (_transaction.value != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Value);
}
if (_transaction.maxFeePerGas != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.MaxFeePerGas);
}
if (_transaction.maxPriorityFeePerGas != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.MaxPriorityFeePerGas);
}
if (_transaction.reserved[0] != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Reserved0);
}
if (_transaction.reserved[1] > type(uint160).max) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Reserved1);
}
if (_transaction.reserved[2] != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Reserved2);
}
if (_transaction.reserved[3] != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Reserved3);
}
if (_transaction.signature.length != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Signature);
}
if (_transaction.paymasterInput.length != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.PaymasterInput);
}
if (_transaction.reservedDynamic.length != 0) {
revert InvalidUpgradeTxn(UpgradeTxVerifyParam.ReservedDynamic);
}
}
/// @dev Calculates the approximate minimum gas limit required for executing a priority transaction.
/// @param _encodingLength The length of the priority transaction encoding in bytes.
/// @param _numberOfFactoryDependencies The number of new factory dependencies that will be added.
/// @param _l2GasPricePerPubdata The L2 gas price for publishing the priority transaction on L2.
/// @return The minimum gas limit required to execute the priority transaction.
/// Note: The calculation includes the main cost of the priority transaction, however, in reality, the operator can spend a little more gas on overheads.
function getMinimalPriorityTransactionGasLimit(
uint256 _encodingLength,
uint256 _numberOfFactoryDependencies,
uint256 _l2GasPricePerPubdata
) internal pure returns (uint256) {
uint256 costForComputation;
{
// Adding the intrinsic cost for the transaction, i.e. auxiliary prices which cannot be easily accounted for
costForComputation = L1_TX_INTRINSIC_L2_GAS;
// Taking into account the hashing costs that depend on the length of the transaction
// Note that L1_TX_DELTA_544_ENCODING_BYTES is the delta in the price for every 544 bytes of
// the transaction's encoding. It is taken as LCM between 136 and 32 (the length for each keccak256 round
// and the size of each new encoding word).
costForComputation += Math.ceilDiv(_encodingLength * L1_TX_DELTA_544_ENCODING_BYTES, 544);
// Taking into the account the additional costs of providing new factory dependencies
costForComputation += _numberOfFactoryDependencies * L1_TX_DELTA_FACTORY_DEPS_L2_GAS;
// There is a minimal amount of computational L2 gas that the transaction should cover
costForComputation = Math.max(costForComputation, L1_TX_MIN_L2_GAS_BASE);
}
uint256 costForPubdata = 0;
{
// Adding the intrinsic cost for the transaction, i.e. auxiliary prices which cannot be easily accounted for
costForPubdata = L1_TX_INTRINSIC_PUBDATA * _l2GasPricePerPubdata;
// Taking into the account the additional costs of providing new factory dependencies
costForPubdata += _numberOfFactoryDependencies * L1_TX_DELTA_FACTORY_DEPS_PUBDATA * _l2GasPricePerPubdata;
}
return costForComputation + costForPubdata;
}
/// @notice Based on the full L2 gas limit (that includes the batch overhead) and other
/// properties of the transaction, returns the l2GasLimit for the body of the transaction (the actual execution).
/// @param _totalGasLimit The L2 gas limit that includes both the overhead for processing the batch
/// and the L2 gas needed to process the transaction itself (i.e. the actual l2GasLimit that will be used for the transaction).
/// @param _encodingLength The length of the ABI-encoding of the transaction.
function getTransactionBodyGasLimit(
uint256 _totalGasLimit,
uint256 _encodingLength
) internal pure returns (uint256 txBodyGasLimit) {
uint256 overhead = getOverheadForTransaction(_encodingLength);
// provided gas limit doesn't cover transaction overhead
if (_totalGasLimit < overhead) {
revert TxnBodyGasLimitNotEnoughGas();
}
unchecked {
// We enforce the fact that `_totalGasLimit >= overhead` explicitly above.
txBodyGasLimit = _totalGasLimit - overhead;
}
}
/// @notice Based on the total L2 gas limit and several other parameters of the transaction
/// returns the part of the L2 gas that will be spent on the batch's overhead.
/// @dev The details of how this function works can be checked in the documentation
/// of the fee model of ZKsync. The appropriate comments are also present
/// in the Rust implementation description of function `get_maximal_allowed_overhead`.
/// @param _encodingLength The length of the binary encoding of the transaction in bytes
function getOverheadForTransaction(
uint256 _encodingLength
) internal pure returns (uint256 batchOverheadForTransaction) {
// The overhead from taking up the transaction's slot
batchOverheadForTransaction = TX_SLOT_OVERHEAD_L2_GAS;
// The overhead for occupying the bootloader memory can be derived from encoded_len
uint256 overheadForLength = MEMORY_OVERHEAD_GAS * _encodingLength;
batchOverheadForTransaction = Math.max(batchOverheadForTransaction, overheadForLength);
}
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
/// @dev The enum that represents the transaction execution status
/// @param Failure The transaction execution failed
/// @param Success The transaction execution succeeded
enum TxStatus {
Failure,
Success
}
/// @dev The log passed from L2
/// @param l2ShardId The shard identifier, 0 - rollup, 1 - porter
/// All other values are not used but are reserved for the future
/// @param isService A boolean flag that is part of the log along with `key`, `value`, and `sender` address.
/// This field is required formally but does not have any special meaning
/// @param txNumberInBatch The L2 transaction number in a Batch, in which the log was sent
/// @param sender The L2 address which sent the log
/// @param key The 32 bytes of information that was sent in the log
/// @param value The 32 bytes of information that was sent in the log
// Both `key` and `value` are arbitrary 32-bytes selected by the log sender
struct L2Log {
uint8 l2ShardId;
bool isService;
uint16 txNumberInBatch;
address sender;
bytes32 key;
bytes32 value;
}
/// @dev An arbitrary length message passed from L2
/// @notice Under the hood it is `L2Log` sent from the special system L2 contract
/// @param txNumberInBatch The L2 transaction number in a Batch, in which the message was sent
/// @param sender The address of the L2 account from which the message was passed
/// @param data An arbitrary length message
struct L2Message {
uint16 txNumberInBatch;
address sender;
bytes data;
}
/// @dev Internal structure that contains the parameters for the writePriorityOp
/// internal function.
/// @param txId The id of the priority transaction.
/// @param l2GasPrice The gas price for the l2 priority operation.
/// @param expirationTimestamp The timestamp by which the priority operation must be processed by the operator.
/// @param request The external calldata request for the priority operation.
struct WritePriorityOpParams {
uint256 txId;
uint256 l2GasPrice;
uint64 expirationTimestamp;
BridgehubL2TransactionRequest request;
}
/// @dev Structure that includes all fields of the L2 transaction
/// @dev The hash of this structure is the "canonical L2 transaction hash" and can
/// be used as a unique identifier of a tx
/// @param txType The tx type number, depending on which the L2 transaction can be
/// interpreted differently
/// @param from The sender's address. `uint256` type for possible address format changes
/// and maintaining backward compatibility
/// @param to The recipient's address. `uint256` type for possible address format changes
/// and maintaining backward compatibility
/// @param gasLimit The L2 gas limit for L2 transaction. Analog to the `gasLimit` on an
/// L1 transactions
/// @param gasPerPubdataByteLimit Maximum number of L2 gas that will cost one byte of pubdata
/// (every piece of data that will be stored on L1 as calldata)
/// @param maxFeePerGas The absolute maximum sender willing to pay per unit of L2 gas to get
/// the transaction included in a Batch. Analog to the EIP-1559 `maxFeePerGas` on an L1 transactions
/// @param maxPriorityFeePerGas The additional fee that is paid directly to the validator
/// to incentivize them to include the transaction in a Batch. Analog to the EIP-1559
/// `maxPriorityFeePerGas` on an L1 transactions
/// @param paymaster The address of the EIP-4337 paymaster, that will pay fees for the
/// transaction. `uint256` type for possible address format changes and maintaining backward compatibility
/// @param nonce The nonce of the transaction. For L1->L2 transactions it is the priority
/// operation Id
/// @param value The value to pass with the transaction
/// @param reserved The fixed-length fields for usage in a future extension of transaction
/// formats
/// @param data The calldata that is transmitted for the transaction call
/// @param signature An abstract set of bytes that are used for transaction authorization
/// @param factoryDeps The set of L2 bytecode hashes whose preimages were shown on L1
/// @param paymasterInput The arbitrary-length data that is used as a calldata to the paymaster pre-call
/// @param reservedDynamic The arbitrary-length field for usage in a future extension of transaction formats
struct L2CanonicalTransaction {
uint256 txType;
uint256 from;
uint256 to;
uint256 gasLimit;
uint256 gasPerPubdataByteLimit;
uint256 maxFeePerGas;
uint256 maxPriorityFeePerGas;
uint256 paymaster;
uint256 nonce;
uint256 value;
// In the future, we might want to add some
// new fields to the struct. The `txData` struct
// is to be passed to account and any changes to its structure
// would mean a breaking change to these accounts. To prevent this,
// we should keep some fields as "reserved"
// It is also recommended that their length is fixed, since
// it would allow easier proof integration (in case we will need
// some special circuit for preprocessing transactions)
uint256[4] reserved;
bytes data;
bytes signature;
uint256[] factoryDeps;
bytes paymasterInput;
// Reserved dynamic type for the future use-case. Using it should be avoided,
// But it is still here, just in case we want to enable some additional functionality
bytes reservedDynamic;
}
/// @param sender The sender's address.
/// @param contractAddressL2 The address of the contract on L2 to call.
/// @param valueToMint The amount of base token that should be minted on L2 as the result of this transaction.
/// @param l2Value The msg.value of the L2 transaction.
/// @param l2Calldata The calldata for the L2 transaction.
/// @param l2GasLimit The limit of the L2 gas for the L2 transaction
/// @param l2GasPerPubdataByteLimit The price for a single pubdata byte in L2 gas.
/// @param factoryDeps The array of L2 bytecodes that the tx depends on.
/// @param refundRecipient The recipient of the refund for the transaction on L2. If the transaction fails, then
/// this address will receive the `l2Value`.
// solhint-disable-next-line gas-struct-packing
struct BridgehubL2TransactionRequest {
address sender;
address contractL2;
uint256 mintValue;
uint256 l2Value;
bytes l2Calldata;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
bytes[] factoryDeps;
address refundRecipient;
}
/// @dev The structure that contains the parameters for the message root
/// @param chainId The chain id of the dependency chain
/// @param blockOrBatchNumber The block number or the batch number where the message root was created
/// For proof based interop it is block number. For commit based interop it is batch number.
/// @param sides The sides of the dynamic incremental merkle tree emitted in the L2ToL1Messenger for precommit based interop
/// For proof and commit based interop, the sides contain a single root.
struct InteropRoot {
uint256 chainId;
uint256 blockOrBatchNumber;
// We are double overloading this. The sides of the dynamic incremental merkle tree normally contains the root, as well as the sides of the tree.
// Second overloading: if the length is 1, we are importing a chainBatchRoot/messageRoot instead of sides.
bytes32[] sides;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {Merkle} from "./Merkle.sol";
import {L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, SUPPORTED_PROOF_METADATA_VERSION} from "../Config.sol";
import {MerklePathEmpty} from "../L1ContractErrors.sol";
import {UncheckedMath} from "./UncheckedMath.sol";
import {UnsupportedProofMetadataVersion} from "../../state-transition/L1StateTransitionErrors.sol";
import {HashedLogIsDefault, InvalidProofLengthForFinalNode} from "../../common/L1ContractErrors.sol";
bytes32 constant BATCH_LEAF_PADDING = keccak256("zkSync:BatchLeaf");
bytes32 constant CHAIN_ID_LEAF_PADDING = keccak256("zkSync:ChainIdLeaf");
struct ProofData {
uint256 settlementLayerChainId;
uint256 settlementLayerBatchNumber;
uint256 settlementLayerBatchRootMask;
uint256 batchLeafProofLen;
bytes32 batchSettlementRoot;
bytes32 chainIdLeaf;
uint256 ptr;
bool finalProofNode;
}
library MessageHashing {
using UncheckedMath for uint256;
/// @dev Returns the leaf hash for a chain with batch number and batch root.
/// @param batchRoot The root hash of the batch.
/// @param batchNumber The number of the batch.
function batchLeafHash(bytes32 batchRoot, uint256 batchNumber) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(BATCH_LEAF_PADDING, batchRoot, batchNumber));
}
/// @dev Returns the leaf hash for a chain with chain root and chain id.
/// @param chainIdRoot The root hash of the chain.
/// @param chainId The id of the chain.
function chainIdLeafHash(bytes32 chainIdRoot, uint256 chainId) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(CHAIN_ID_LEAF_PADDING, chainIdRoot, chainId));
}
struct ProofMetadata {
uint256 proofStartIndex;
uint256 logLeafProofLen;
uint256 batchLeafProofLen;
bool finalProofNode;
}
/// @notice Parses the proof metadata.
/// @param _proof The proof.
/// @return result The proof metadata.
function parseProofMetadata(bytes32[] calldata _proof) internal pure returns (ProofMetadata memory result) {
bytes32 proofMetadata = _proof[0];
// We support two formats of the proofs:
// 1. The old format, where `_proof` is just a plain Merkle proof.
// 2. The new format, where the first element of the `_proof` is encoded metadata, which consists of the following:
// - first byte: metadata version (0x01).
// - second byte: length of the log leaf proof (the proof that the log belongs to a batch).
// - third byte: length of the batch leaf proof (the proof that the batch belongs to another settlement layer, if any).
// - fourth byte: whether the current proof is the last in the links of recursive proofs for settlement layers.
// - the rest of the bytes are zeroes.
//
// In the future the old version will be disabled, and only the new version will be supported.
// For now, we need to support both for backwards compatibility. We distinguish between those based on whether the last 28 bytes are zeroes.
// It is safe, since the elements of the proof are hashes and are unlikely to have 28 zero bytes in them.
// We shift left by 4 bytes = 32 bits to remove the top 32 bits of the metadata.
uint256 metadataAsUint256 = (uint256(proofMetadata) << 32);
if (metadataAsUint256 == 0) {
// It is the new version
bytes1 metadataVersion = bytes1(proofMetadata);
if (uint256(uint8(metadataVersion)) != SUPPORTED_PROOF_METADATA_VERSION) {
revert UnsupportedProofMetadataVersion(uint256(uint8(metadataVersion)));
}
result.proofStartIndex = 1;
result.logLeafProofLen = uint256(uint8(proofMetadata[1]));
result.batchLeafProofLen = uint256(uint8(proofMetadata[2]));
result.finalProofNode = uint256(uint8(proofMetadata[3])) != 0;
} else {
// It is the old version
// The entire proof is a merkle path
result.proofStartIndex = 0;
result.logLeafProofLen = _proof.length;
result.batchLeafProofLen = 0;
result.finalProofNode = true;
}
if (result.finalProofNode && result.batchLeafProofLen != 0) {
revert InvalidProofLengthForFinalNode();
}
}
/// @notice Parses and processes the proof and returns the resulting data.
/// @param _chainId The chain id of the L2 where the leaf comes from.
/// @param _batchNumber The batch number.
/// @param _leafProofMask The leaf proof mask.
/// @param _leaf The leaf to be proven.
/// @param _proof The proof.
/// @return result The proof verification result.
function _getProofData(
uint256 _chainId,
uint256 _batchNumber,
uint256 _leafProofMask,
bytes32 _leaf,
bytes32[] calldata _proof
) internal pure returns (ProofData memory result) {
if (_proof.length == 0) {
revert MerklePathEmpty();
}
// Check that hashed log is not the default one,
// otherwise it means that the value is out of range of sent L2 -> L1 logs
if (_leaf == L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH) {
revert HashedLogIsDefault();
}
ProofMetadata memory proofMetadata = MessageHashing.parseProofMetadata(_proof);
result.ptr = proofMetadata.proofStartIndex;
{
bytes32 batchSettlementRoot = Merkle.calculateRootMemory(
extractSlice(_proof, result.ptr, result.ptr + proofMetadata.logLeafProofLen),
_leafProofMask,
_leaf
);
result.ptr += proofMetadata.logLeafProofLen;
result.batchSettlementRoot = batchSettlementRoot;
result.finalProofNode = proofMetadata.finalProofNode;
if (proofMetadata.finalProofNode) {
return result;
}
// Now, we'll have to check that the Gateway included the message.
bytes32 localBatchLeafHash = MessageHashing.batchLeafHash(batchSettlementRoot, _batchNumber);
uint256 batchLeafProofMask = uint256(bytes32(_proof[result.ptr]));
++result.ptr;
bytes32 chainIdRoot = Merkle.calculateRootMemory(
extractSlice(_proof, result.ptr, result.ptr + proofMetadata.batchLeafProofLen),
batchLeafProofMask,
localBatchLeafHash
);
result.ptr += proofMetadata.batchLeafProofLen;
result.chainIdLeaf = MessageHashing.chainIdLeafHash(chainIdRoot, _chainId);
}
uint256 settlementLayerChainId;
uint256 settlementLayerBatchNumber;
uint256 settlementLayerBatchRootMask;
// Preventing stack too deep error
{
// Now, we just need to double check whether this chainId leaf was present in the tree.
uint256 settlementLayerPackedBatchInfo = uint256(_proof[result.ptr]);
++result.ptr;
settlementLayerBatchNumber = uint256(settlementLayerPackedBatchInfo >> 128);
settlementLayerBatchRootMask = uint256(settlementLayerPackedBatchInfo & ((1 << 128) - 1));
settlementLayerChainId = uint256(_proof[result.ptr]);
++result.ptr;
}
result = ProofData({
settlementLayerChainId: settlementLayerChainId,
settlementLayerBatchNumber: settlementLayerBatchNumber,
settlementLayerBatchRootMask: settlementLayerBatchRootMask,
batchLeafProofLen: proofMetadata.batchLeafProofLen,
batchSettlementRoot: result.batchSettlementRoot,
chainIdLeaf: result.chainIdLeaf,
ptr: result.ptr,
finalProofNode: proofMetadata.finalProofNode
});
}
/// @notice Extracts slice from the proof.
/// @param _proof The proof.
/// @param _left The left index.
/// @param _right The right index.
/// @return slice The slice.
function extractSlice(
bytes32[] calldata _proof,
uint256 _left,
uint256 _right
) internal pure returns (bytes32[] memory slice) {
slice = new bytes32[](_right - _left);
for (uint256 i = _left; i < _right; i = i.uncheckedInc()) {
slice[i - _left] = _proof[i];
}
}
/// @notice Extracts slice until the end of the array.
/// @dev It is used in one place in order to circumvent the stack too deep error.
function extractSliceUntilEnd(
bytes32[] calldata _proof,
uint256 _start
) internal pure returns (bytes32[] memory slice) {
slice = extractSlice(_proof, _start, _proof.length);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IVerifier, VerifierParams} from "../chain-interfaces/IVerifier.sol";
import {PriorityQueue} from "../../state-transition/libraries/PriorityQueue.sol";
import {PriorityTree} from "../../state-transition/libraries/PriorityTree.sol";
/// @notice Indicates whether an upgrade is initiated and if yes what type
/// @param None Upgrade is NOT initiated
/// @param Transparent Fully transparent upgrade is initiated, upgrade data is publicly known
/// @param Shadow Shadow upgrade is initiated, upgrade data is hidden
enum UpgradeState {
None,
Transparent,
Shadow
}
/// @dev Logically separated part of the storage structure, which is responsible for everything related to proxy
/// upgrades and diamond cuts
/// @param proposedUpgradeHash The hash of the current upgrade proposal, zero if there is no active proposal
/// @param state Indicates whether an upgrade is initiated and if yes what type
/// @param securityCouncil Address which has the permission to approve instant upgrades (expected to be a Gnosis
/// multisig)
/// @param approvedBySecurityCouncil Indicates whether the security council has approved the upgrade
/// @param proposedUpgradeTimestamp The timestamp when the upgrade was proposed, zero if there are no active proposals
/// @param currentProposalId The serial number of proposed upgrades, increments when proposing a new one
struct UpgradeStorage {
bytes32 proposedUpgradeHash;
UpgradeState state;
address securityCouncil;
bool approvedBySecurityCouncil;
uint40 proposedUpgradeTimestamp;
uint40 currentProposalId;
}
/// @notice The struct that describes whether users will be charged for pubdata for L1->L2 transactions.
/// @param Rollup The users are charged for pubdata & it is priced based on the gas price on Ethereum.
/// @param Validium The pubdata is considered free with regard to the L1 gas price.
enum PubdataPricingMode {
Rollup,
Validium
}
/// @notice The fee params for L1->L2 transactions for the network.
/// @param pubdataPricingMode How the users will charged for pubdata in L1->L2 transactions.
/// @param batchOverheadL1Gas The amount of L1 gas required to process the batch (except for the calldata).
/// @param maxPubdataPerBatch The maximal number of pubdata that can be emitted per batch.
/// @param priorityTxMaxPubdata The maximal amount of pubdata a priority transaction is allowed to publish.
/// It can be slightly less than maxPubdataPerBatch in order to have some margin for the bootloader execution.
/// @param minimalL2GasPrice The minimal L2 gas price to be used by L1->L2 transactions. It should represent
/// the price that a single unit of compute costs.
struct FeeParams {
PubdataPricingMode pubdataPricingMode;
uint32 batchOverheadL1Gas;
uint32 maxPubdataPerBatch;
uint32 maxL2GasPerBatch;
uint32 priorityTxMaxPubdata;
uint64 minimalL2GasPrice;
}
/// @dev storing all storage variables for ZK chain diamond facets
/// NOTE: It is used in a proxy, so it is possible to add new variables to the end
/// but NOT to modify already existing variables or change their order.
/// NOTE: variables prefixed with '__DEPRECATED_' are deprecated and shouldn't be used.
/// Their presence is maintained for compatibility and to prevent storage collision.
// solhint-disable-next-line gas-struct-packing
struct ZKChainStorage {
/// @dev Storage of variables needed for deprecated diamond cut facet
uint256[7] __DEPRECATED_diamondCutStorage;
/// @notice Address which will exercise critical changes to the Diamond Proxy (upgrades, freezing & unfreezing). Replaced by CTM
address __DEPRECATED_governor;
/// @notice Address that the governor proposed as one that will replace it
address __DEPRECATED_pendingGovernor;
/// @notice List of permitted validators
mapping(address validatorAddress => bool isValidator) validators;
/// @dev Verifier contract. Used to verify aggregated proof for batches
IVerifier verifier;
/// @notice Total number of executed batches i.e. batches[totalBatchesExecuted] points at the latest executed batch
/// (batch 0 is genesis)
uint256 totalBatchesExecuted;
/// @notice Total number of proved batches i.e. batches[totalBatchesProved] points at the latest proved batch
uint256 totalBatchesVerified;
/// @notice Total number of committed batches i.e. batches[totalBatchesCommitted] points at the latest committed
/// batch
uint256 totalBatchesCommitted;
/// @dev Stored hashed StoredBatch for batch number
mapping(uint256 batchNumber => bytes32 batchHash) storedBatchHashes;
/// @dev Stored root hashes of L2 -> L1 logs
mapping(uint256 batchNumber => bytes32 l2LogsRootHash) l2LogsRootHashes;
/// @dev Container that stores transactions requested from L1
PriorityQueue.Queue __DEPRECATED_priorityQueue;
/// @dev The smart contract that manages the list with permission to call contract functions
address __DEPRECATED_allowList;
VerifierParams __DEPRECATED_verifierParams;
/// @notice Bytecode hash of bootloader program.
/// @dev Used as an input to zkp-circuit.
bytes32 l2BootloaderBytecodeHash;
/// @notice Bytecode hash of default account (bytecode for EOA).
/// @dev Used as an input to zkp-circuit.
bytes32 l2DefaultAccountBytecodeHash;
/// @dev Indicates that the porter may be touched on L2 transactions.
/// @dev Used as an input to zkp-circuit.
bool zkPorterIsAvailable;
/// @dev The maximum number of the L2 gas that a user can request for L1 -> L2 transactions
/// @dev This is the maximum number of L2 gas that is available for the "body" of the transaction, i.e.
/// without overhead for proving the batch.
uint256 priorityTxMaxGasLimit;
/// @dev Storage of variables needed for upgrade facet
UpgradeStorage __DEPRECATED_upgrades;
/// @dev A mapping L2 batch number => message number => flag.
/// @dev The L2 -> L1 log is sent for every withdrawal, so this mapping is serving as
/// a flag to indicate that the message was already processed.
/// @dev Used to indicate that eth withdrawal was already processed
mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized)) isEthWithdrawalFinalized;
/// @dev The most recent withdrawal time and amount reset
uint256 __DEPRECATED_lastWithdrawalLimitReset;
/// @dev The accumulated withdrawn amount during the withdrawal limit window
uint256 __DEPRECATED_withdrawnAmountInWindow;
/// @dev A mapping user address => the total deposited amount by the user
mapping(address => uint256) __DEPRECATED_totalDepositedAmountPerUser;
/// @dev Stores the protocol version. Note, that the protocol version may not only encompass changes to the
/// smart contracts, but also to the node behavior.
uint256 protocolVersion;
/// @dev Hash of the system contract upgrade transaction. If 0, then no upgrade transaction needs to be done.
bytes32 l2SystemContractsUpgradeTxHash;
/// @dev Batch number where the upgrade transaction has happened. If 0, then no upgrade transaction has happened
/// yet.
uint256 l2SystemContractsUpgradeBatchNumber;
/// @dev Address which will exercise non-critical changes to the Diamond Proxy (changing validator set & unfreezing)
address admin;
/// @notice Address that the admin proposed as one that will replace admin role
address pendingAdmin;
/// @dev Fee params used to derive gasPrice for the L1->L2 transactions. For L2 transactions,
/// the bootloader gives enough freedom to the operator.
/// @dev The value is only for the L1 deployment of the ZK Chain, since payment for all the priority transactions is
/// charged at that level.
FeeParams feeParams;
/// @dev Address of the blob versioned hash getter smart contract used for EIP-4844 versioned hashes.
address __DEPRECATED_blobVersionedHashRetriever;
/// @dev The chainId of the chain
uint256 chainId;
/// @dev The address of the bridgehub
address bridgehub;
/// @dev The address of the ChainTypeManager
address chainTypeManager;
/// @dev The address of the baseToken contract. Eth is address(1)
address __DEPRECATED_baseToken;
/// @dev The address of the baseTokenbridge. Eth also uses the shared bridge
address __DEPRECATED_baseTokenBridge;
/// @notice gasPriceMultiplier for each baseToken, so that each L1->L2 transaction pays for its transaction on the destination
/// we multiply by the nominator, and divide by the denominator
uint128 baseTokenGasPriceMultiplierNominator;
uint128 baseTokenGasPriceMultiplierDenominator;
/// @dev The optional address of the contract that has to be used for transaction filtering/whitelisting
address transactionFilterer;
/// @dev The address of the l1DAValidator contract.
/// This contract is responsible for the verification of the correctness of the DA on L1.
address l1DAValidator;
/// @dev The address of the contract on L2 that is responsible for the data availability verification.
/// This contract sends `l2DAValidatorOutputHash` to L1 via L2->L1 system log and it will routed to the `l1DAValidator` contract.
address l2DAValidator;
/// @dev the Asset Id of the baseToken
bytes32 baseTokenAssetId;
/// @dev If this ZKchain settles on this chain, then this is zero. Otherwise it is the address of the ZKchain that is a
/// settlement layer for this ZKchain. (think about it as a 'forwarding' address for the chain that migrated away).
address settlementLayer;
/// @dev Priority tree, the new data structure for priority queue
PriorityTree.Tree priorityTree;
/// @dev Whether the chain is a permanent rollup. Note, that it only enforces the DA validator pair, but
/// it does not enforce any other parameters, e.g. `pubdataPricingMode`
bool isPermanentRollup;
/// @notice Bytecode hash of evm emulator.
/// @dev Used as an input to zkp-circuit.
bytes32 l2EvmEmulatorBytecodeHash;
/// @notice The precommitment for the latest uncommitted batch (i.e. totalBatchesCommitted + 1).
/// @dev Whenever the `totalBatchesCommitted` changes, this variable is reset to `DEFAULT_PRECOMMITMENT_FOR_THE_LAST_BATCH`
/// (the value of the constant can be found in Config.sol).
bytes32 precommitmentForTheLatestBatch;
}// SPDX-License-Identifier: MIT // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version. pragma solidity ^0.8.21; /** * @author Matter Labs * @custom:security-contact [email protected] * @notice The library for unchecked math. */ library UncheckedMath { function uncheckedInc(uint256 _number) internal pure returns (uint256) { unchecked { return _number + 1; } } function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) { unchecked { return _lhs + _rhs; } } }
// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {BytecodeError, LengthIsNotDivisibleBy32, MalformedBytecode} from "../L1ContractErrors.sol";
import {UncheckedMath} from "../libraries/UncheckedMath.sol";
import {L2_TO_L1_MESSENGER_SYSTEM_CONTRACT} from "./L2ContractAddresses.sol";
/**
* @author Matter Labs
* @custom:security-contact [email protected]
* @notice Interface for the contract that is used to deploy contracts on L2.
*/
interface IContractDeployer {
/// @notice A struct that describes a forced deployment on an address.
/// @param bytecodeHash The bytecode hash to put on an address.
/// @param newAddress The address on which to deploy the bytecodehash to.
/// @param callConstructor Whether to run the constructor on the force deployment.
/// @param value The `msg.value` with which to initialize a contract.
/// @param input The constructor calldata.
struct ForceDeployment {
bytes32 bytecodeHash;
address newAddress;
bool callConstructor;
uint256 value;
bytes input;
}
/// @notice This method is to be used only during an upgrade to set bytecodes on specific addresses.
/// @param _deployParams A set of parameters describing force deployment.
function forceDeployOnAddresses(ForceDeployment[] calldata _deployParams) external payable;
/// @notice Creates a new contract at a determined address using the `CREATE2` salt on L2
/// @param _salt a unique value to create the deterministic address of the new contract
/// @param _bytecodeHash the bytecodehash of the new contract to be deployed
/// @param _input the calldata to be sent to the constructor of the new contract
function create2(bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input) external returns (address);
}
/**
* @author Matter Labs
* @custom:security-contact [email protected]
* @notice Helper library for working with L2 contracts on L1.
*/
library L2ContractHelper {
using UncheckedMath for uint256;
/// @dev The prefix used to create CREATE2 addresses.
bytes32 private constant CREATE2_PREFIX = keccak256("zksyncCreate2");
/// @dev Prefix used during derivation of account addresses using CREATE
/// @dev keccak256("zksyncCreate")
bytes32 private constant CREATE_PREFIX = 0x63bae3a9951d38e8a3fbb7b70909afc1200610fc5bc55ade242f815974674f23;
/// @notice Sends L2 -> L1 arbitrary-long message through the system contract messenger.
/// @param _message Data to be sent to L1.
/// @return keccak256 hash of the sent message.
function sendMessageToL1(bytes memory _message) internal returns (bytes32) {
return L2_TO_L1_MESSENGER_SYSTEM_CONTRACT.sendToL1(_message);
}
/// @notice Validate the bytecode format and calculate its hash.
/// @param _bytecode The bytecode to hash.
/// @return hashedBytecode The 32-byte hash of the bytecode.
/// Note: The function reverts the execution if the bytecode has non expected format:
/// - Bytecode bytes length is not a multiple of 32
/// - Bytecode bytes length is not less than 2^21 bytes (2^16 words)
/// - Bytecode words length is not odd
function hashL2Bytecode(bytes memory _bytecode) internal pure returns (bytes32 hashedBytecode) {
// Note that the length of the bytecode must be provided in 32-byte words.
if (_bytecode.length % 32 != 0) {
revert LengthIsNotDivisibleBy32(_bytecode.length);
}
uint256 bytecodeLenInWords = _bytecode.length / 32;
// bytecode length must be less than 2^16 words
if (bytecodeLenInWords >= 2 ** 16) {
revert MalformedBytecode(BytecodeError.NumberOfWords);
}
// bytecode length in words must be odd
if (bytecodeLenInWords % 2 == 0) {
revert MalformedBytecode(BytecodeError.WordsMustBeOdd);
}
hashedBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
// Setting the version of the hash
hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248)));
// Setting the length
hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224);
}
/// @notice Validate the bytecode format and calculate its hash.
/// @param _bytecode The bytecode to hash.
/// @return hashedBytecode The 32-byte hash of the bytecode.
/// Note: The function reverts the execution if the bytecode has non expected format:
/// - Bytecode bytes length is not a multiple of 32
/// - Bytecode bytes length is not less than 2^21 bytes (2^16 words)
/// - Bytecode words length is not odd
function hashL2BytecodeCalldata(bytes calldata _bytecode) internal pure returns (bytes32 hashedBytecode) {
// Note that the length of the bytecode must be provided in 32-byte words.
if (_bytecode.length % 32 != 0) {
revert LengthIsNotDivisibleBy32(_bytecode.length);
}
uint256 bytecodeLenInWords = _bytecode.length / 32;
// bytecode length must be less than 2^16 words
if (bytecodeLenInWords >= 2 ** 16) {
revert MalformedBytecode(BytecodeError.NumberOfWords);
}
// bytecode length in words must be odd
if (bytecodeLenInWords % 2 == 0) {
revert MalformedBytecode(BytecodeError.WordsMustBeOdd);
}
hashedBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
// Setting the version of the hash
hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248)));
// Setting the length
hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224);
}
/// @notice Validates the format of the given bytecode hash.
/// @dev Due to the specification of the L2 bytecode hash, not every 32 bytes could be a legit bytecode hash.
/// @dev The function reverts on invalid bytecode hash format.
/// @param _bytecodeHash The hash of the bytecode to validate.
function validateBytecodeHash(bytes32 _bytecodeHash) internal pure {
uint8 version = uint8(_bytecodeHash[0]);
// Incorrectly formatted bytecodeHash
if (version != 1 || _bytecodeHash[1] != bytes1(0)) {
revert MalformedBytecode(BytecodeError.Version);
}
// Code length in words must be odd
if (bytecodeLen(_bytecodeHash) % 2 == 0) {
revert MalformedBytecode(BytecodeError.WordsMustBeOdd);
}
}
/// @notice Returns the length of the bytecode associated with the given hash.
/// @param _bytecodeHash The hash of the bytecode.
/// @return codeLengthInWords The length of the bytecode in words.
function bytecodeLen(bytes32 _bytecodeHash) internal pure returns (uint256 codeLengthInWords) {
codeLengthInWords = uint256(uint8(_bytecodeHash[2])) * 256 + uint256(uint8(_bytecodeHash[3]));
}
/// @notice Computes the create2 address for a Layer 2 contract.
/// @param _sender The address of the sender.
/// @param _salt The salt value to use in the create2 address computation.
/// @param _bytecodeHash The contract bytecode hash.
/// @param _constructorInputHash The hash of the constructor input data.
/// @return The create2 address of the contract.
/// NOTE: L2 create2 derivation is different from L1 derivation!
function computeCreate2Address(
address _sender,
bytes32 _salt,
bytes32 _bytecodeHash,
bytes32 _constructorInputHash
) internal pure returns (address) {
bytes32 senderBytes = bytes32(uint256(uint160(_sender)));
bytes32 data = keccak256(
// solhint-disable-next-line func-named-parameters
bytes.concat(CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash)
);
return address(uint160(uint256(data)));
}
/// @notice Calculates the address of a deployed contract via create
/// @param _sender The account that deploys the contract.
/// @param _senderNonce The deploy nonce of the sender's account.
/// NOTE: L2 create derivation is different from L1 derivation!
function computeCreateAddress(address _sender, uint256 _senderNonce) internal pure returns (address) {
// No collision is possible with the Ethereum's CREATE, since
// the prefix begins with 0x63....
bytes32 hash = keccak256(
bytes.concat(CREATE_PREFIX, bytes32(uint256(uint160(_sender))), bytes32(_senderNonce))
);
return address(uint160(uint256(hash)));
}
/// @notice Hashes the L2 bytecodes and returns them in the format in which they are processed by the bootloader
function hashFactoryDeps(bytes[] memory _factoryDeps) internal pure returns (uint256[] memory hashedFactoryDeps) {
uint256 factoryDepsLen = _factoryDeps.length;
hashedFactoryDeps = new uint256[](factoryDepsLen);
for (uint256 i = 0; i < factoryDepsLen; i = i.uncheckedInc()) {
bytes32 hashedBytecode = hashL2Bytecode(_factoryDeps[i]);
// Store the resulting hash sequentially in words.
assembly {
mstore(add(hashedFactoryDeps, mul(add(i, 1), 32)), hashedBytecode)
}
}
}
}// SPDX-License-Identifier: Apache-2.0
/*
* Copyright 2019-2021, Offchain Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
library AddressAliasHelper {
uint160 private constant offset = uint160(0x1111000000000000000000000000000000001111);
/// @notice Utility function converts the address that submitted a tx
/// to the inbox on L1 to the msg.sender viewed on L2
/// @param l1Address the address in the L1 that triggered the tx to L2
/// @return l2Address L2 address as viewed in msg.sender
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
unchecked {
l2Address = address(uint160(l1Address) + offset);
}
}
/// @notice Utility function that converts the msg.sender viewed on L2 to the
/// address that submitted a tx to the inbox on L1
/// @param l2Address L2 address as viewed in msg.sender
/// @return l1Address the address in the L1 that triggered the tx to L2
function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
unchecked {
l1Address = address(uint160(l2Address) - offset);
}
}
/// @notice Utility function used to calculate the correct refund recipient
/// @param _refundRecipient the address that should receive the refund
/// @param _originalCaller the address that triggered the tx to L2
/// @return _recipient the corrected address that should receive the refund
function actualRefundRecipient(
address _refundRecipient,
address _originalCaller
) internal view returns (address _recipient) {
if (_refundRecipient == address(0)) {
// If the `_refundRecipient` is not provided, we use the `_originalCaller` as the recipient.
// solhint-disable avoid-tx-origin
// slither-disable-next-line tx-origin
_recipient = _originalCaller == tx.origin
? _originalCaller
: AddressAliasHelper.applyL1ToL2Alias(_originalCaller);
// solhint-enable avoid-tx-origin
} else if (_refundRecipient.code.length > 0) {
// If the `_refundRecipient` is a smart contract, we apply the L1 to L2 alias to prevent foot guns.
_recipient = AddressAliasHelper.applyL1ToL2Alias(_refundRecipient);
} else {
_recipient = _refundRecipient;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {ZKChainStorage} from "../ZKChainStorage.sol";
import {ReentrancyGuard} from "../../../common/ReentrancyGuard.sol";
import {PriorityQueue} from "../../libraries/PriorityQueue.sol";
import {PriorityTree} from "../../libraries/PriorityTree.sol";
import {NotSettlementLayer} from "../../L1StateTransitionErrors.sol";
import {Unauthorized} from "../../../common/L1ContractErrors.sol";
import {IBridgehub} from "../../../bridgehub/IBridgehub.sol";
/// @title Base contract containing functions accessible to the other facets.
/// @author Matter Labs
/// @custom:security-contact [email protected]
contract ZKChainBase is ReentrancyGuard {
using PriorityQueue for PriorityQueue.Queue;
using PriorityTree for PriorityTree.Tree;
// slither-disable-next-line uninitialized-state
ZKChainStorage internal s;
/// @notice Checks that the message sender is an active admin
modifier onlyAdmin() {
if (msg.sender != s.admin) {
revert Unauthorized(msg.sender);
}
_;
}
/// @notice Checks if validator is active
modifier onlyValidator() {
if (!s.validators[msg.sender]) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlyChainTypeManager() {
if (msg.sender != s.chainTypeManager) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlyBridgehub() {
if (msg.sender != s.bridgehub) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlyChainAssetHandler() {
if (msg.sender != IBridgehub(s.bridgehub).chainAssetHandler()) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlyAdminOrChainTypeManager() {
if (msg.sender != s.admin && msg.sender != s.chainTypeManager) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlyValidatorOrChainTypeManager() {
if (!s.validators[msg.sender] && msg.sender != s.chainTypeManager) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlySettlementLayer() {
if (s.settlementLayer != address(0)) {
revert NotSettlementLayer();
}
_;
}
modifier onlySelf() {
if (msg.sender != address(this)) {
revert Unauthorized(msg.sender);
}
_;
}
/// @notice Returns whether the priority queue is still active, i.e.
/// the chain has not processed all transactions from it
function _isPriorityQueueActive() internal view returns (bool) {
return s.__DEPRECATED_priorityQueue.getFirstUnprocessedPriorityTx() < s.priorityTree.startIndex;
}
/// @notice Ensures that the queue is deactivated. Should be invoked
/// whenever the chain migrates to another settlement layer.
function _forceDeactivateQueue() internal {
// We double check whether it is still active mainly to prevent
// overriding `tail`/`head` on L1 deployment.
if (_isPriorityQueueActive()) {
uint256 startIndex = s.priorityTree.startIndex;
s.__DEPRECATED_priorityQueue.head = startIndex;
s.__DEPRECATED_priorityQueue.tail = startIndex;
}
}
function _getTotalPriorityTxs() internal view returns (uint256) {
return s.priorityTree.getTotalPriorityTxs();
}
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
/// @dev `keccak256("")`
bytes32 constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
/// @dev Bytes in raw L2 log
/// @dev Equal to the bytes size of the tuple - (uint8 ShardId, bool isService, uint16 txNumberInBatch, address sender,
/// bytes32 key, bytes32 value)
uint256 constant L2_TO_L1_LOG_SERIALIZE_SIZE = 88;
/// @dev The maximum length of the bytes array with L2 -> L1 logs
uint256 constant MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES = 4 + L2_TO_L1_LOG_SERIALIZE_SIZE * 512;
/// @dev The value of default leaf hash for L2 -> L1 logs Merkle tree
/// @dev An incomplete fixed-size tree is filled with this value to be a full binary tree
/// @dev Actually equal to the `keccak256(new bytes(L2_TO_L1_LOG_SERIALIZE_SIZE))`
bytes32 constant L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH = 0x72abee45b59e344af8a6e520241c4744aff26ed411f4c4b00f8af09adada43ba;
bytes32 constant DEFAULT_L2_LOGS_TREE_ROOT_HASH = bytes32(0);
/// @dev Denotes the type of the ZKsync transaction that came from L1.
uint256 constant PRIORITY_OPERATION_L2_TX_TYPE = 255;
/// @dev Denotes the type of the ZKsync transaction that is used for system upgrades.
uint256 constant SYSTEM_UPGRADE_L2_TX_TYPE = 254;
/// @dev The maximal allowed difference between protocol minor versions in an upgrade. The 100 gap is needed
/// in case a protocol version has been tested on testnet, but then not launched on mainnet, e.g.
/// due to a bug found.
/// We are allowed to jump at most 100 minor versions at a time. The major version is always expected to be 0.
uint256 constant MAX_ALLOWED_MINOR_VERSION_DELTA = 100;
/// @dev The amount of time in seconds the validator has to process the priority transaction
/// NOTE: The constant is set to zero for the Alpha release period
uint256 constant PRIORITY_EXPIRATION = 0 days;
// @dev The chainId of Ethereum Mainnet
uint256 constant MAINNET_CHAIN_ID = 1;
/// @dev Timestamp - seconds since unix epoch. This value will be used on the mainnet.
uint256 constant MAINNET_COMMIT_TIMESTAMP_NOT_OLDER = 3 days;
/// @dev Timestamp - seconds since unix epoch. This value will be used on testnets.
uint256 constant TESTNET_COMMIT_TIMESTAMP_NOT_OLDER = 30 days;
/// @dev Maximum available error between real commit batch 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 - 12 seconds)
uint256 constant COMMIT_TIMESTAMP_APPROXIMATION_DELTA = 1 hours;
/// @dev Shift to apply to verify public input before verifying.
uint256 constant PUBLIC_INPUT_SHIFT = 32;
/// @dev The maximum number of L2 gas that a user can request for an L2 transaction
uint256 constant MAX_GAS_PER_TRANSACTION = 80_000_000;
/// @dev Even though the price for 1 byte of pubdata is 16 L1 gas, we have a slightly increased
/// value.
uint256 constant L1_GAS_PER_PUBDATA_BYTE = 17;
/// @dev The intrinsic cost of the L1->l2 transaction in computational L2 gas
uint256 constant L1_TX_INTRINSIC_L2_GAS = 167_157;
/// @dev The intrinsic cost of the L1->l2 transaction in pubdata
uint256 constant L1_TX_INTRINSIC_PUBDATA = 88;
/// @dev The minimal base price for L1 transaction
uint256 constant L1_TX_MIN_L2_GAS_BASE = 173_484;
/// @dev The number of L2 gas the transaction starts costing more with each 544 bytes of encoding
uint256 constant L1_TX_DELTA_544_ENCODING_BYTES = 1656;
/// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
uint256 constant L1_TX_DELTA_FACTORY_DEPS_L2_GAS = 2473;
/// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
uint256 constant L1_TX_DELTA_FACTORY_DEPS_PUBDATA = 64;
/// @dev The number of pubdata an L1->L2 transaction requires with each new factory dependency
uint256 constant MAX_NEW_FACTORY_DEPS = 64;
/// @dev The L2 gasPricePerPubdata required to be used in bridges.
uint256 constant REQUIRED_L2_GAS_PRICE_PER_PUBDATA = 800;
/// @dev The mask which should be applied to the packed batch and L2 block timestamp in order
/// to obtain the L2 block timestamp. Applying this mask is equivalent to calculating modulo 2**128
uint256 constant PACKED_L2_BLOCK_TIMESTAMP_MASK = 0xffffffffffffffffffffffffffffffff;
/// @dev Address of the point evaluation precompile used for EIP-4844 blob verification.
address constant POINT_EVALUATION_PRECOMPILE_ADDR = address(0x0A);
/// @dev The overhead for a transaction slot in L2 gas.
/// It is roughly equal to 80kk/MAX_TRANSACTIONS_IN_BATCH, i.e. how many gas would an L1->L2 transaction
/// need to pay to compensate for the batch being closed.
/// @dev It is expected that the L1 contracts will enforce that the L2 gas price will be high enough to compensate
/// the operator in case the batch is closed because of tx slots filling up.
uint256 constant TX_SLOT_OVERHEAD_L2_GAS = 10000;
/// @dev The overhead for each byte of the bootloader memory that the encoding of the transaction.
/// It is roughly equal to 80kk/BOOTLOADER_MEMORY_FOR_TXS, i.e. how many gas would an L1->L2 transaction
/// need to pay to compensate for the batch being closed.
/// @dev It is expected that the L1 contracts will enforce that the L2 gas price will be high enough to compensate
/// the operator in case the batch is closed because of the memory for transactions being filled up.
uint256 constant MEMORY_OVERHEAD_GAS = 10;
/// @dev The maximum gas limit for a priority transaction in L2.
uint256 constant PRIORITY_TX_MAX_GAS_LIMIT = 72_000_000;
/// @dev the address used to identify eth as the base token for chains.
address constant ETH_TOKEN_ADDRESS = address(1);
/// @dev the value returned in bridgehubDeposit in the TwoBridges function.
bytes32 constant TWO_BRIDGES_MAGIC_VALUE = bytes32(uint256(keccak256("TWO_BRIDGES_MAGIC_VALUE")) - 1);
/// @dev https://eips.ethereum.org/EIPS/eip-1352
address constant BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS = address(uint160(type(uint16).max));
/// @dev the maximum number of supported chains, this is an arbitrary limit.
/// @dev Note, that in case of a malicious Bridgehub admin, the total number of chains
/// can be up to 2 times higher. This may be possible, in case the old ChainTypeManager
/// had `100` chains and these were migrated to the Bridgehub only after `MAX_NUMBER_OF_ZK_CHAINS`
/// were added to the bridgehub via creation of new chains.
uint256 constant MAX_NUMBER_OF_ZK_CHAINS = 100;
/// @dev Used as the `msg.sender` for transactions that relayed via a settlement layer.
address constant SETTLEMENT_LAYER_RELAY_SENDER = address(uint160(0x1111111111111111111111111111111111111111));
/// @dev The metadata version that is supported by the ZK Chains to prove that an L2->L1 log was included in a batch.
uint256 constant SUPPORTED_PROOF_METADATA_VERSION = 1;
/// @dev The virtual address of the L1 settlement layer.
address constant L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS = address(
uint160(uint256(keccak256("L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS")) - 1)
);
struct PriorityTreeCommitment {
uint256 nextLeafIndex;
uint256 startIndex;
uint256 unprocessedIndex;
bytes32[] sides;
}
// Info that allows to restore a chain.
struct ZKChainCommitment {
/// @notice Total number of executed batches i.e. batches[totalBatchesExecuted] points at the latest executed batch
/// (batch 0 is genesis)
uint256 totalBatchesExecuted;
/// @notice Total number of proved batches i.e. batches[totalBatchesProved] points at the latest proved batch
uint256 totalBatchesVerified;
/// @notice Total number of committed batches i.e. batches[totalBatchesCommitted] points at the latest committed
/// batch
uint256 totalBatchesCommitted;
/// @notice The hash of the L2 system contracts ugpgrade transaction.
/// @dev It is non zero if the migration happens while the upgrade is not yet finalized.
bytes32 l2SystemContractsUpgradeTxHash;
/// @notice The batch when the system contracts upgrade transaction was executed.
/// @dev It is non-zero if the migration happens while the batch where the upgrade tx was present
/// has not been finalized (executed) yet.
uint256 l2SystemContractsUpgradeBatchNumber;
/// @notice The hashes of the batches that are needed to keep the blockchain working.
/// @dev The length of the array is equal to the `totalBatchesCommitted - totalBatchesExecuted + 1`, i.e. we need
/// to store all the unexecuted batches' hashes + 1 latest executed one.
bytes32[] batchHashes;
/// @notice Commitment to the priority merkle tree.
PriorityTreeCommitment priorityTree;
/// @notice Whether a chain is a permanent rollup.
bool isPermanentRollup;
/// @notice The precommitment to the transactions of the latest batch.
bytes32 precommitmentForTheLatestBatch;
}
/// @dev Used as the `msg.sender` for system service transactions.
address constant SERVICE_TRANSACTION_SENDER = address(uint160(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF));
/// @dev To avoid higher costs the writes, we avoid making the slot zero.
/// This ensures that the cost of writes is always 5k and avoids the 20k initial write from the non-zero value.
bytes32 constant DEFAULT_PRECOMMITMENT_FOR_THE_LAST_BATCH = bytes32(uint256(1));
/// @dev The length of a packed transaction precommitment in bytes. It consists of two parts: 32-byte tx hash and 1-byte status (0 or 1).
uint256 constant PACKED_L2_PRECOMMITMENT_LENGTH = 33;// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {IL2ToL1Messenger} from "./IL2ToL1Messenger.sol";
import {IL2InteropRootStorage} from "../interfaces/IL2InteropRootStorage.sol";
import {IMessageVerification} from "../../state-transition/chain-interfaces/IMessageVerification.sol";
/// @dev the offset for the system contracts
uint160 constant SYSTEM_CONTRACTS_OFFSET = 0x8000; // 2^15
/// @dev The offset from which the built-in, but user space contracts are located.
uint160 constant USER_CONTRACTS_OFFSET = 0x10000; // 2^16
/// @dev The formal address of the initial program of the system: the bootloader
address constant L2_BOOTLOADER_ADDRESS = address(SYSTEM_CONTRACTS_OFFSET + 0x01);
/// @dev The address of the known code storage system contract
address constant L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x04);
/// @dev The address of the L2 deployer system contract.
address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x06);
/// @dev The special reserved L2 address. It is located in the system contracts space but doesn't have deployed
/// bytecode.
/// @dev The L2 deployer system contract allows changing bytecodes on any address if the `msg.sender` is this address.
/// @dev So, whenever the governor wants to redeploy system contracts, it just initiates the L1 upgrade call deployer
/// system contract
/// via the L1 -> L2 transaction with `sender == L2_FORCE_DEPLOYER_ADDR`. For more details see the
/// `diamond-initializers` contracts.
address constant L2_FORCE_DEPLOYER_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x07);
/// @dev The address of the L2ToL1Messenger system contract
address constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x08);
/// @dev The address of the special smart contract that can send arbitrary length message as an L2 log
IL2ToL1Messenger constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT = IL2ToL1Messenger(
L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR
);
/// @dev The address of the eth token system contract
address constant L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x0a);
/// @dev The address of the context system contract
address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x0b);
/// @dev The address of the pubdata chunk publisher contract
address constant L2_PUBDATA_CHUNK_PUBLISHER_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x11);
/// @dev The address used to execute complex upgragedes, also used for the genesis upgrade
address constant L2_COMPLEX_UPGRADER_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x0f);
/// @dev the address of the msg value system contract
address constant MSG_VALUE_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x09);
/// @dev The address of the create2 factory contract
address constant L2_CREATE2_FACTORY_ADDR = address(USER_CONTRACTS_OFFSET + 0x00);
/// @dev The address used to execute the genesis upgrade
address constant L2_GENESIS_UPGRADE_ADDR = address(USER_CONTRACTS_OFFSET + 0x01);
/// @dev The genesis upgrade address is reused for all version specific upgrades
address constant L2_VERSION_SPECIFIC_UPGRADER_ADDR = L2_GENESIS_UPGRADE_ADDR;
/// @dev The address of the L2 bridge hub system contract, used to start L1->L2 transactions
address constant L2_BRIDGEHUB_ADDR = address(USER_CONTRACTS_OFFSET + 0x02);
/// @dev the address of the l2 asset router.
address constant L2_ASSET_ROUTER_ADDR = address(USER_CONTRACTS_OFFSET + 0x03);
/// @dev An l2 system contract address, used in the assetId calculation for native assets.
/// This is needed for automatic bridging, i.e. without deploying the AssetHandler contract,
/// if the assetId can be calculated with this address then it is in fact an NTV asset
address constant L2_NATIVE_TOKEN_VAULT_ADDR = address(USER_CONTRACTS_OFFSET + 0x04);
/// @dev the address of the l2 asset router.
address constant L2_MESSAGE_ROOT_ADDR = address(USER_CONTRACTS_OFFSET + 0x05);
/// @dev The address of the SloadContract system contract, which provides a method to read values from arbitrary storage slots
address constant SLOAD_CONTRACT_ADDR = address(USER_CONTRACTS_OFFSET + 0x06);
/// @dev The address of the WETH implementation contract
address constant L2_WETH_IMPL_ADDR = address(USER_CONTRACTS_OFFSET + 0x07);
/// @dev The address of the L2 interop root storage system contract
IL2InteropRootStorage constant L2_INTEROP_ROOT_STORAGE = IL2InteropRootStorage(address(USER_CONTRACTS_OFFSET + 0x08));
/// @dev The address of the L2 message verification system contract
IMessageVerification constant L2_MESSAGE_VERIFICATION = IMessageVerification(address(USER_CONTRACTS_OFFSET + 0x09));
/// @dev The address of the L2 chain handler system contract
address constant L2_CHAIN_ASSET_HANDLER_ADDR = address(USER_CONTRACTS_OFFSET + 0x0a);// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol";
import {INativeTokenVault} from "../ntv/INativeTokenVault.sol";
import {IAssetRouterBase} from "./IAssetRouterBase.sol";
import {L2TransactionRequestTwoBridgesInner} from "../../bridgehub/IBridgehub.sol";
import {IL1SharedBridgeLegacy} from "../interfaces/IL1SharedBridgeLegacy.sol";
import {IL1ERC20Bridge} from "../interfaces/IL1ERC20Bridge.sol";
/// @title L1 Bridge contract interface
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IL1AssetRouter is IAssetRouterBase, IL1SharedBridgeLegacy {
event BridgehubMintData(bytes bridgeMintData);
event BridgehubDepositFinalized(
uint256 indexed chainId,
bytes32 indexed txDataHash,
bytes32 indexed l2DepositTxHash
);
event ClaimedFailedDepositAssetRouter(uint256 indexed chainId, bytes32 indexed assetId, bytes assetData);
event AssetDeploymentTrackerSet(
bytes32 indexed assetId,
address indexed assetDeploymentTracker,
bytes32 indexed additionalData
);
event LegacyDepositInitiated(
uint256 indexed chainId,
bytes32 indexed l2DepositTxHash,
address indexed from,
address to,
address l1Token,
uint256 amount
);
/// @notice Initiates a deposit by locking funds on the contract and sending the request
/// of processing an L2 transaction where tokens would be minted.
/// @dev If the token is bridged for the first time, the L2 token contract will be deployed. Note however, that the
/// newly-deployed token does not support any custom logic, i.e. rebase tokens' functionality is not supported.
/// @param _originalCaller The `msg.sender` address from the external call that initiated current one.
/// @param _l2Receiver The account address that should receive funds on L2.
/// @param _l1Token The L1 token address which is deposited.
/// @param _amount The total amount of tokens to be bridged.
/// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction.
/// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction.
/// @param _refundRecipient The address on L2 that will receive the refund for the transaction.
/// @dev If the L2 deposit finalization transaction fails, the `_refundRecipient` will receive the `_l2Value`.
/// Please note, the contract may change the refund recipient's address to eliminate sending funds to addresses
/// out of control.
/// - If `_refundRecipient` is a contract on L1, the refund will be sent to the aliased `_refundRecipient`.
/// - If `_refundRecipient` is set to `address(0)` and the sender has NO deployed bytecode on L1, the refund will
/// be sent to the `msg.sender` address.
/// - If `_refundRecipient` is set to `address(0)` and the sender has deployed bytecode on L1, the refund will be
/// sent to the aliased `msg.sender` address.
/// @dev The address aliasing of L1 contracts as refund recipient on L2 is necessary to guarantee that the funds
/// are controllable through the Mailbox, since the Mailbox applies address aliasing to the from address for the
/// L2 tx if the L1 msg.sender is a contract. Without address aliasing for L1 contracts as refund recipients they
/// would not be able to make proper L2 tx requests through the Mailbox to use or withdraw the funds from L2, and
/// the funds would be lost.
/// @return txHash The L2 transaction hash of deposit finalization.
function depositLegacyErc20Bridge(
address _originalCaller,
address _l2Receiver,
address _l1Token,
uint256 _amount,
uint256 _l2TxGasLimit,
uint256 _l2TxGasPerPubdataByte,
address _refundRecipient
) external payable returns (bytes32 txHash);
function L1_NULLIFIER() external view returns (IL1Nullifier);
function L1_WETH_TOKEN() external view returns (address);
function ETH_TOKEN_ASSET_ID() external view returns (bytes32);
function nativeTokenVault() external view returns (INativeTokenVault);
function setAssetDeploymentTracker(bytes32 _assetRegistrationData, address _assetDeploymentTracker) external;
function setNativeTokenVault(INativeTokenVault _nativeTokenVault) external;
function setL1Erc20Bridge(IL1ERC20Bridge _legacyBridge) external;
/// @notice Withdraw funds from the initiated deposit, that failed when finalizing on L2.
/// @param _chainId The ZK chain id to which the deposit was initiated.
/// @param _depositSender The address of the entity that initiated the deposit.
/// @param _assetId The unique identifier of the deposited L1 token.
/// @param _assetData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. Might include extra information.
/// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system.
function bridgeRecoverFailedTransfer(
uint256 _chainId,
address _depositSender,
bytes32 _assetId,
bytes calldata _assetData
) external;
/// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2.
/// @param _chainId The ZK chain id to which deposit was initiated.
/// @param _depositSender The address of the entity that initiated the deposit.
/// @param _assetId The unique identifier of the deposited L1 token.
/// @param _assetData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. Might include extra information.
/// @param _l2TxHash The L2 transaction hash of the failed deposit finalization.
/// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed.
/// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
/// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent.
/// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization.
/// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system.
function bridgeRecoverFailedTransfer(
uint256 _chainId,
address _depositSender,
bytes32 _assetId,
bytes memory _assetData,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external;
/// @notice Transfers funds to Native Token Vault, if the asset is registered with it. Does nothing for ETH or non-registered tokens.
/// @dev assetId is not the padded address, but the correct encoded id (NTV stores respective format for IDs)
/// @param _amount The asset amount to be transferred to native token vault.
/// @param _originalCaller The `msg.sender` address from the external call that initiated current one.
function transferFundsToNTV(bytes32 _assetId, uint256 _amount, address _originalCaller) external returns (bool);
/// @notice Finalize the withdrawal and release funds
/// @param _chainId The chain ID of the transaction to check
/// @param _l2BatchNumber The L2 batch number where the withdrawal was processed
/// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message
/// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent
/// @param _message The L2 withdraw data, stored in an L2 -> L1 message
/// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization
function finalizeWithdrawal(
uint256 _chainId,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes calldata _message,
bytes32[] calldata _merkleProof
) external;
/// @notice Initiates a transfer transaction within Bridgehub, used by `requestL2TransactionTwoBridges`.
/// @param _chainId The chain ID of the ZK chain to which deposit.
/// @param _originalCaller The `msg.sender` address from the external call that initiated current one.
/// @param _value The `msg.value` on the target chain tx.
/// @param _data The calldata for the second bridge deposit.
/// @return request The data used by the bridgehub to create L2 transaction request to specific ZK chain.
/// @dev Data has the following abi encoding for legacy deposits:
/// address _l1Token,
/// uint256 _amount,
/// address _l2Receiver
/// for new deposits:
/// bytes32 _assetId,
/// bytes _transferData
function bridgehubDeposit(
uint256 _chainId,
address _originalCaller,
uint256 _value,
bytes calldata _data
) external payable returns (L2TransactionRequestTwoBridgesInner memory request);
/// @notice Generates a calldata for calling the deposit finalization on the L2 native token contract.
// / @param _chainId The chain ID of the ZK chain to which deposit.
/// @param _sender The address of the deposit initiator.
/// @param _assetId The deposited asset ID.
/// @param _assetData The encoded data, which is used by the asset handler to determine L2 recipient and amount. Might include extra information.
/// @return Returns calldata used on ZK chain.
function getDepositCalldata(
address _sender,
bytes32 _assetId,
bytes memory _assetData
) external view returns (bytes memory);
/// @notice Allows bridgehub to acquire mintValue for L1->L2 transactions.
/// @dev If the corresponding L2 transaction fails, refunds are issued to a refund recipient on L2.
/// @param _chainId The chain ID of the ZK chain to which deposit.
/// @param _assetId The deposited asset ID.
/// @param _originalCaller The `msg.sender` address from the external call that initiated current one.
/// @param _amount The total amount of tokens to be bridged.
function bridgehubDepositBaseToken(
uint256 _chainId,
bytes32 _assetId,
address _originalCaller,
uint256 _amount
) external payable;
/// @notice Routes the confirmation to nullifier for backward compatibility.
/// @notice Confirms the acceptance of a transaction by the Mailbox, as part of the L2 transaction process within Bridgehub.
/// This function is utilized by `requestL2TransactionTwoBridges` to validate the execution of a transaction.
/// @param _chainId The chain ID of the ZK chain to which confirm the deposit.
/// @param _txDataHash The keccak256 hash of 0x01 || abi.encode(bytes32, bytes) to identify deposits.
/// @param _txHash The hash of the L1->L2 transaction to confirm the deposit.
function bridgehubConfirmL2Transaction(uint256 _chainId, bytes32 _txDataHash, bytes32 _txHash) external;
function isWithdrawalFinalized(
uint256 _chainId,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex
) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
// 0x5ecf2d7a
error AccessToFallbackDenied(address target, address invoker);
// 0x3995f750
error AccessToFunctionDenied(address target, bytes4 selector, address invoker);
// 0x0dfb42bf
error AddressAlreadySet(address addr);
// 0x86bb51b8
error AddressHasNoCode(address);
// 0x1f73225f
error AddressMismatch(address expected, address supplied);
// 0x2a5989a0
error AlreadyPermanentRollup();
// 0x0bfcef28
error AlreadyWhitelisted(address);
// 0x5e85ae73
error AmountMustBeGreaterThanZero();
// 0xfde974f4
error AssetHandlerDoesNotExist(bytes32 assetId);
// 0x64107968
error AssetHandlerNotRegistered(bytes32 assetId);
// 0xfe919e28
error AssetIdAlreadyRegistered();
// 0x1294e9e1
error AssetIdMismatch(bytes32 expected, bytes32 supplied);
// 0x04a0b7e9
error AssetIdNotSupported(bytes32 assetId);
// 0x11832de8
error AssetRouterAllowanceNotZero();
// 0x6ef9a972
error BaseTokenGasPriceDenominatorNotSet();
// 0x55ad3fd3
error BatchHashMismatch(bytes32 expected, bytes32 actual);
// 0x2078a6a0
error BatchNotExecuted(uint256 batchNumber);
// 0xbd4455ff
error BatchNumberMismatch(uint256 expectedBatchNumber, uint256 providedBatchNumber);
// 0x6cf12312
error BridgeHubAlreadyRegistered();
// 0xdb538614
error BridgeMintNotImplemented();
// 0xaa5f6180
error BurningNativeWETHNotSupported();
// 0xccdd18d2
error BytecodeAlreadyPublished(bytes32 bytecodeHash);
// 0x25d8333c
error CallerNotTimerAdmin();
// 0x3331e9c0
error CallNotAllowed(bytes call);
// 0xe85392f9
error CanOnlyProcessOneBatch();
// 0x00c6ead2
error CantExecuteUnprovenBatches();
// 0xe18cb383
error CantRevertExecutedBatch();
// 0x78d2ed02
error ChainAlreadyLive();
// 0x24591d89
error ChainIdAlreadyExists();
// 0x717a1656
error ChainIdCantBeCurrentChain();
// 0xa179f8c9
error ChainIdMismatch();
// 0x23f3c357
error ChainIdNotRegistered(uint256 chainId);
// 0x8f620a06
error ChainIdTooBig();
// 0xec273439
error CTMAlreadyRegistered();
// 0xc630ef3c
error CTMNotRegistered();
// 0x907f8e51
error DeadlineNotYetPassed();
// 0xf2885eb3
error DefaultAdminTransferNotAllowed();
// 0xf7a01e4d
error DelegateCallFailed(bytes returnData);
// 0x0a8ed92c
error DenominatorIsZero();
// 0xb4f54111
error DeployFailed();
// 0x138ee1a3
error DeployingBridgedTokenForNativeToken();
// 0xc7c9660f
error DepositDoesNotExist();
// 0xad2fa98e
error DepositExists();
// 0x0e7ee319
error DiamondAlreadyFrozen();
// 0xa7151b9a
error DiamondNotFrozen();
// 0x7138356f
error EmptyAddress();
// 0x2d4d012f
error EmptyAssetId();
// 0x1c25715b
error EmptyBytes32();
// 0x99d8fec9
error EmptyData();
// 0x95b66fe9
error EmptyDeposit();
// 0x84286507
error EmptyPrecommitData(uint256 batchNumber);
// 0x456f8f7a
error EmptyProofLength();
// 0x627e0872
error ETHDepositNotSupported();
// 0xac4a3f98
error FacetExists(bytes4 selector, address);
// 0xc91cf3b1
error GasPerPubdataMismatch();
// 0x6d4a7df8
error GenesisBatchCommitmentZero();
// 0x7940c83f
error GenesisBatchHashZero();
// 0xb4fc6835
error GenesisIndexStorageZero();
// 0x3a1a8589
error GenesisUpgradeZero();
// 0xd356e6ba
error HashedLogIsDefault();
// 0x0b08d5be
error HashMismatch(bytes32 expected, bytes32 actual);
// 0xd7d93e1f
error IncorrectBatchBounds(
uint256 processFromExpected,
uint256 processToExpected,
uint256 processFromProvided,
uint256 processToProvided
);
// 0xdd381a4c
error IncorrectBridgeHubAddress(address bridgehub);
// 0x1929b7de
error IncorrectTokenAddressFromNTV(bytes32 assetId, address tokenAddress);
// 0x826fb11e
error InsufficientChainBalance();
// 0x9bf8b9aa
error InvalidBatchNumber(uint256 provided, uint256 expected);
// 0xcbd9d2e0
error InvalidCaller(address);
// 0x92daded2
error InvalidDAForPermanentRollup();
// 0x4fbe5dba
error InvalidDelay();
// 0xc1780bd6
error InvalidLogSender(address sender, uint256 logKey);
// 0xa1ec1876
error InvalidMessageRoot(bytes32 expectedMessageRoot, bytes32 providedMessageRoot);
// 0xde4c0b96
error InvalidNTVBurnData();
// 0xd8e9405c
error InvalidNumberOfBlobs(uint256 expected, uint256 numCommitments, uint256 numHashes);
// 0x99f6cc22
error InvalidPackedPrecommitmentLength(uint256 length);
// 0x09bde339
error InvalidProof();
// 0x48c5fa28
error InvalidProofLengthForFinalNode();
// 0x5428eae7
error InvalidProtocolVersion();
// 0x6f1cf752
error InvalidPubdataPricingMode();
// 0x12ba286f
error InvalidSelector(bytes4 func);
// 0xbe7193d4
error InvalidSystemLogsLength();
// 0x5f1aa154
error InvalidUpgradeTxn(UpgradeTxVerifyParam);
// 0xfb5c22e6
error L2TimestampTooBig();
// 0x97e1359e
error L2WithdrawalMessageWrongLength(uint256 messageLen);
// 0x8efef97a
error LegacyBridgeNotSet();
// 0x29963361
error LegacyBridgeUsesNonNativeToken();
// 0xfade089a
error LegacyEncodingUsedForNonL1Token();
// 0x767eed08
error LegacyMethodForNonL1Token();
// 0xe37d2c02
error LengthIsNotDivisibleBy32(uint256 length);
// 0x1b6825bb
error LogAlreadyProcessed(uint8);
// 0x43e266b0
error MalformedBytecode(BytecodeError);
// 0xafbb7a4e
error MerkleIndexOrHeightMismatch();
// 0x9bb54c35
error MerkleIndexOutOfBounds();
// 0xc33e6128
error MerkleNothingToProve();
// 0x8e23ac1a
error MerklePathEmpty();
// 0x09aa9830
error MerklePathLengthMismatch(uint256 pathLength, uint256 expectedLength);
// 0x1c500385
error MerklePathOutOfBounds();
// 0x1b582fcf
error MerkleWrongIndex(uint256 index, uint256 maxNodeNumber);
// 0x485cfcaa
error MerkleWrongLength(uint256 newLeavesLength, uint256 leafNumber);
// 0x3312a450
error MigrationPaused();
// 0x4e98b356
error MigrationsNotPaused();
// 0xfa44b527
error MissingSystemLogs(uint256 expected, uint256 actual);
// 0x4a094431
error MsgValueMismatch(uint256 expectedMsgValue, uint256 providedMsgValue);
// 0xb385a3da
error MsgValueTooLow(uint256 required, uint256 provided);
// 0x8b7e144a
error NewDeadlineExceedsMaxDeadline();
// 0x6eef58d1
error NewDeadlineNotGreaterThanCurrent();
// 0x79cc2d22
error NoCallsProvided();
// 0xce63ce17
error NoCTMForAssetId(bytes32 assetId);
// 0xa6fef710
error NoFunctionsForDiamondCut();
// 0xcab098d8
error NoFundsTransferred();
// 0xb20b58ce
error NoLegacySharedBridge();
// 0xc21b1ab7
error NonEmptyCalldata();
// 0x536ec84b
error NonEmptyMsgValue();
// 0xd018e08e
error NonIncreasingTimestamp();
// 0x0105f9c0
error NonSequentialBatch();
// 0x0ac76f01
error NonSequentialVersion();
// 0xfa5cd00f
error NotAllowed(address addr);
// 0x64846fe4
error NotARestriction(address addr);
// 0xb49df1f2
error NotAZKChain(address addr);
// 0xdd7e3621
error NotInitializedReentrancyGuard();
// 0xdf17e316
error NotWhitelisted(address);
// 0xf3ed9dfa
error OnlyEraSupported();
// 0x6c167909
error OnlySelfAllowed();
// 0x1a21feed
error OperationExists();
// 0xeda2fbb1
error OperationMustBePending();
// 0xe1c1ff37
error OperationMustBeReady();
// 0xb926450e
error OriginChainIdNotFound();
// 0x688c63e5
error PrecommitmentMismatch(uint256 batchNumber, bytes32 expected, bytes32 found);
// 0x9b48e060
error PreviousOperationNotExecuted();
// 0xd5a99014
error PriorityOperationsRollingHashMismatch();
// 0x1a4d284a
error PriorityTxPubdataExceedsMaxPubDataPerBatch();
// 0xa461f651
error ProtocolIdMismatch(uint256 expectedProtocolVersion, uint256 providedProtocolId);
// 0x64f94ec2
error ProtocolIdNotGreater();
// 0x959f26fb
error PubdataGreaterThanLimit(uint256 limit, uint256 length);
// 0x63c36549
error QueueIsEmpty();
// 0xab143c06
error Reentrancy();
// 0x667d17de
error RemoveFunctionFacetAddressNotZero(address facet);
// 0xa2d4b16c
error RemoveFunctionFacetAddressZero();
// 0xf6fd7071
error RemovingPermanentRestriction();
// 0x3580370c
error ReplaceFunctionFacetAddressZero();
// 0xf126e113
error RestrictionWasAlreadyPresent(address restriction);
// 0x52e22c98
error RestrictionWasNotPresent(address restriction);
// 0x9a67c1cb
error RevertedBatchNotAfterNewLastBatch();
// 0xfe0aa4f2
error RoleAccessDenied(address chainAddress, bytes32 role, address account);
// 0xd3b6535b
error SelectorsMustAllHaveSameFreezability();
// 0x02181a13
error SettlementLayersMustSettleOnL1();
// 0x856d5b77
error SharedBridgeNotSet();
// 0x7774d2f9
error SharedBridgeValueNotSet(SharedBridgeKey);
// 0xdf3a8fdd
error SlotOccupied();
// 0xae43b424
error SystemLogsSizeTooBig();
// 0x08753982
error TimeNotReached(uint256 expectedTimestamp, uint256 actualTimestamp);
// 0x7a4902ad
error TimerAlreadyStarted();
// 0x2d50c33b
error TimestampError();
// 0xa51fa558
error TokenIsLegacy();
// 0x1850b46b
error TokenNotLegacy();
// 0x06439c6b
error TokenNotSupported(address token);
// 0x23830e28
error TokensWithFeesNotSupported();
// 0x8e3ce3cb
error TooHighDeploymentNonce();
// 0x76da24b9
error TooManyFactoryDeps();
// 0xf0b4e88f
error TooMuchGas();
// 0x00c5a6a9
error TransactionNotAllowed();
// 0x4c991078
error TxHashMismatch();
// 0x2e311df8
error TxnBodyGasLimitNotEnoughGas();
// 0xfcb9b2e1
error UnallowedImplementation(bytes32 implementationHash);
// 0x8e4a23d6
error Unauthorized(address caller);
// 0xe52478c7
error UndefinedDiamondCutAction();
// 0x6aa39880
error UnexpectedSystemLog(uint256 logKey);
// 0xc352bb73
error UnknownVerifierType();
// 0xf3dd1b9c
error UnsupportedCommitBatchEncoding(uint8 version);
// 0x084a1449
error UnsupportedEncodingVersion();
// 0x14d2ed8a
error UnsupportedExecuteBatchEncoding(uint8 version);
// 0xf338f830
error UnsupportedProofBatchEncoding(uint8 version);
// 0xf093c2e5
error UpgradeBatchNumberIsNotZero();
// 0x47b3b145
error ValidateTxnNotEnoughGas();
// 0x626ade30
error ValueMismatch(uint256 expected, uint256 actual);
// 0xe1022469
error VerifiedBatchesExceedsCommittedBatches();
// 0xae899454
error WithdrawalAlreadyFinalized();
// 0x750b219c
error WithdrawFailed();
// 0xf20c5c2a
error WrappedBaseTokenAlreadyRegistered();
// 0x15e8e429
error WrongMagicValue(uint256 expectedMagicValue, uint256 providedMagicValue);
// 0xd92e233d
error ZeroAddress();
// 0xc84885d4
error ZeroChainId();
// 0x601b6882
error ZKChainLimitReached();
enum SharedBridgeKey {
PostUpgradeFirstBatch,
LegacyBridgeFirstBatch,
LegacyBridgeLastDepositBatch,
LegacyBridgeLastDepositTxn
}
enum BytecodeError {
Version,
NumberOfWords,
Length,
WordsMustBeOdd
}
enum UpgradeTxVerifyParam {
From,
To,
Paymaster,
Value,
MaxFeePerGas,
MaxPriorityFeePerGas,
Reserved0,
Reserved1,
Reserved2,
Reserved3,
Signature,
PaymasterInput,
ReservedDynamic
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.21; // 0xb325f767 error AdminZero(); // 0xca1c3cbc error AlreadyMigrated(); // 0x125d99b0 error BlobHashBlobCommitmentMismatchValue(); // 0xafda12bf error CommitBasedInteropNotSupported(); // 0xc02d3ee3 error ContractNotDeployed(); // 0xdf2c5fa5 error DependencyRootsRollingHashMismatch(bytes32 _expected, bytes32 _actual); // 0xedae13f3 error ExecutedIsNotConsistentWithVerified(uint256 batchesExecuted, uint256 batchesVerified); // 0xc866ff2c error InitialForceDeploymentMismatch(bytes32 forceDeploymentHash, bytes32 initialForceDeploymentHash); // 0xfbd630b8 error InvalidBatchesDataLength(uint256 batchesDataLength, uint256 priorityOpsDataLength); // 0x7a47c9a2 error InvalidChainId(); // 0xc06789fa error InvalidCommitment(); // 0xd2531c15 error InvalidL2DAOutputHash(bytes32 l2DAValidatorOutputHash); // 0x30043900 error InvalidNextLeafIndex(uint256 treeNextLeafIndex, uint256 commitmentNextLeafIndex); // 0xfb1a3b59 error InvalidNumberOfBatchHashes(uint256 batchHashesLength, uint256 expected); // 0xbeb96791 error InvalidNumberOfBlobs(uint256 blobsProvided, uint256 maxBlobsSupported); // 0x5513177c error InvalidPubdataHash(bytes32 fullPubdataHash, bytes32 providedPubdataHash); // 0x5717f940 error InvalidPubdataSource(uint8 pubdataSource); // 0x90f67ecf error InvalidStartIndex(uint256 treeStartIndex, uint256 commitmentStartIndex); // 0x0f67bc0a error InvalidUnprocessedIndex(uint256 treeUnprocessedIndex, uint256 commitmentUnprocessedIndex); // 0x2e89f517 error L1DAValidatorAddressIsZero(); // 0x7fbff2dd error L1DAValidatorInvalidSender(address msgSender); // 0x944bc075 error L2DAValidatorAddressIsZero(); // 0xa969e486 error LocalRootIsZero(); // 0xbdaf7d42 error LocalRootMustBeZero(); // 0x9b5f85eb error MessageRootIsZero(); // 0x2237c426 error MismatchL2DAValidator(); // 0x2c01a4af error MismatchNumberOfLayer1Txs(uint256 numberOfLayer1Txs, uint256 expectedLength); // 0xf9ba09d6 error NotAllBatchesExecuted(); // 0xf05c64c6 error NotChainAdmin(address prevMsgSender, address admin); // 0x8fd63d21 error NotEraChain(); // 0xa7050bf6 error NotHistoricalRoot(bytes32); // 0x32ddf9a2 error NotHyperchain(); // 0x87470e36 error NotL1(uint256 blockChainId); // 0xd7b2559b error NotMigrated(); // 0xd0266e26 error NotSettlementLayer(); // 0x04e05fd1 error OnlyOneBlobWithCalldataAllowed(); // 0x885ae069 error OperatorDAInputTooSmall(uint256 operatorDAInputLength, uint256 minAllowedLength); // 0x681150be error OutdatedProtocolVersion(uint256 protocolVersion, uint256 currentProtocolVersion); // 0xc59d372c error ProtocolVersionNotUpToDate(uint256 currentProtocolVersion, uint256 protocolVersion); // 0x2dc9747d error PubdataInputTooSmall(uint256 pubdataInputLength, uint256 totalBlobsCommitmentSize); // 0x9044dff9 error PubdataLengthTooBig(uint256 pubdataLength, uint256 totalBlobSizeBytes); // 0x79274f04 error UnsupportedProofMetadataVersion(uint256 metadataVersion); // 0x52595598 error ValL1DAWrongInputLength(uint256 inputLength, uint256 expectedLength); // 0x712d02d2 error VerifiedIsNotConsistentWithCommitted(uint256 batchesVerified, uint256 batchesCommitted);
// SPDX-License-Identifier: MIT // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version. pragma solidity ^0.8.21; /// @title The interface of the ZKsync contract, responsible for the main ZKsync logic. /// @author Matter Labs /// @custom:security-contact [email protected] interface IZKChainBase { /// @return Returns facet name. function getName() external view returns (string memory); }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {L2Log, L2Message} from "../../../common/Messaging.sol";
import {IMessageVerification} from "../../chain-interfaces/IMessageVerification.sol";
import {L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR} from "../../../common/l2-helpers/L2ContractAddresses.sol";
/// @title The interface of the ZKsync MessageVerification contract that can be used to prove L2 message inclusion.
/// @dev This contract is abstract and is inherited by the Mailbox and L2MessageVerification contracts.
/// @dev All calls go through via the _proveL2LeafInclusion function, which is different on L1 and L2.
/// @author Matter Labs
/// @custom:security-contact [email protected]
abstract contract MessageVerification is IMessageVerification {
/// @inheritdoc IMessageVerification
function proveL2MessageInclusionShared(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _index,
L2Message calldata _message,
bytes32[] calldata _proof
) public view virtual returns (bool) {
return
_proveL2LogInclusion({
_chainId: _chainId,
_blockOrBatchNumber: _blockOrBatchNumber,
_index: _index,
_log: _l2MessageToLog(_message),
_proof: _proof
});
}
/// @inheritdoc IMessageVerification
function proveL2LeafInclusionShared(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _leafProofMask,
bytes32 _leaf,
bytes32[] calldata _proof
) public view virtual override returns (bool) {
return
_proveL2LeafInclusion({
_chainId: _chainId,
_blockOrBatchNumber: _blockOrBatchNumber,
_leafProofMask: _leafProofMask,
_leaf: _leaf,
_proof: _proof
});
}
function _proveL2LeafInclusion(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _leafProofMask,
bytes32 _leaf,
bytes32[] calldata _proof
) internal view virtual returns (bool);
/// @dev Prove that a specific L2 log was sent in a specific L2 batch number
function _proveL2LogInclusion(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _index,
L2Log memory _log,
bytes32[] calldata _proof
) internal view returns (bool) {
bytes32 hashedLog = keccak256(
// solhint-disable-next-line func-named-parameters
abi.encodePacked(_log.l2ShardId, _log.isService, _log.txNumberInBatch, _log.sender, _log.key, _log.value)
);
// It is ok to not check length of `_proof` array, as length
// of leaf preimage (which is `L2_TO_L1_LOG_SERIALIZE_SIZE`) is not
// equal to the length of other nodes preimages (which are `2 * 32`)
// We can use `index` as a mask, since the `LocalLogsRoot` is on the left part of the tree.
return
_proveL2LeafInclusion({
_chainId: _chainId,
_blockOrBatchNumber: _blockOrBatchNumber,
_leafProofMask: _index,
_leaf: hashedLog,
_proof: _proof
});
}
/// @dev Convert arbitrary-length message to the raw L2 log
function _l2MessageToLog(L2Message calldata _message) internal pure returns (L2Log memory) {
return
L2Log({
l2ShardId: 0,
isService: true,
txNumberInBatch: _message.txNumberInBatch,
sender: L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR,
key: bytes32(uint256(uint160(_message.sender))),
value: keccak256(_message.data)
});
}
/// @inheritdoc IMessageVerification
function proveL2LogInclusionShared(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _index,
L2Log calldata _log,
bytes32[] calldata _proof
) public view virtual returns (bool) {
return
_proveL2LogInclusion({
_chainId: _chainId,
_blockOrBatchNumber: _blockOrBatchNumber,
_index: _index,
_log: _log,
_proof: _proof
});
}
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {L2Log, L2Message} from "../../common/Messaging.sol";
/// @title The interface of the ZKsync MessageVerification contract that can be used to prove L2 message inclusion.
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IMessageVerification {
/// @notice Prove that a specific arbitrary-length message was sent in a specific L2 batch/block number.
/// @param _chainId The chain id of the L2 where the message comes from.
/// @param _blockOrBatchNumber The executed L2 batch/block number in which the message appeared.
/// @param _index The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
/// @param _message Information about the sent message: sender address, the message itself, tx index in the L2 batch where the message was sent.
/// @param _proof Merkle proof for inclusion of L2 log that was sent with the message.
/// @return Boolean specifying whether the proof is valid.
function proveL2MessageInclusionShared(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _index,
L2Message calldata _message,
bytes32[] calldata _proof
) external view returns (bool);
/// @notice Prove that a specific L2 log was sent in a specific L2 batch.
/// @param _chainId The chain id of the L2 where the log comes from.
/// @param _blockOrBatchNumber The executed L2 batch/block number in which the log appeared.
/// @param _index The position of the l2log in the L2 logs Merkle tree.
/// @param _log Information about the sent log.
/// @param _proof Merkle proof for inclusion of the L2 log.
/// @return Whether the proof is correct and L2 log is included in batch.
function proveL2LogInclusionShared(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _index,
L2Log calldata _log,
bytes32[] calldata _proof
) external view returns (bool);
/// @dev Proves that a certain leaf was included as part of the log merkle tree.
/// @dev Warning: this function does not enforce any additional checks on the structure
/// of the leaf. This means that it can accept intermediate nodes of the Merkle tree as a `_leaf` as
/// well as the default "empty" leaves. It is the responsibility of the caller to ensure that the
/// `_leaf` is a hash of a valid leaf.
/// @param _chainId The chain id of the L2 where the leaf comes from.
/// @param _blockOrBatchNumber The batch/block number of the leaf to be proven.
/// @param _leafProofMask The leaf proof mask.
/// @param _leaf The leaf to be proven.
/// @param _proof The proof.
function proveL2LeafInclusionShared(
uint256 _chainId,
uint256 _blockOrBatchNumber,
uint256 _leafProofMask,
bytes32 _leaf,
bytes32[] calldata _proof
) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IBridgehub, L2TransactionRequestTwoBridgesInner} from "./IBridgehub.sol";
import {IAssetRouterBase} from "../bridge/asset-router/IAssetRouterBase.sol";
import {IL1AssetDeploymentTracker} from "../bridge/interfaces/IL1AssetDeploymentTracker.sol";
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface ICTMDeploymentTracker is IL1AssetDeploymentTracker {
function bridgehubDeposit(
uint256 _chainId,
address _originalCaller,
uint256 _l2Value,
bytes calldata _data
) external payable returns (L2TransactionRequestTwoBridgesInner memory request);
function BRIDGE_HUB() external view returns (IBridgehub);
function L1_ASSET_ROUTER() external view returns (IAssetRouterBase);
function registerCTMAssetOnL1(address _ctmAddress) external;
function calculateAssetId(address _l1CTM) external view returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IBridgehub} from "./IBridgehub.sol";
/**
* @author Matter Labs
* @notice MessageRoot contract is responsible for storing and aggregating the roots of the batches from different chains into the MessageRoot.
* @custom:security-contact [email protected]
*/
interface IMessageRoot {
function BRIDGE_HUB() external view returns (IBridgehub);
function addNewChain(uint256 _chainId) external;
function addChainBatchRoot(uint256 _chainId, uint256 _batchNumber, bytes32 _chainBatchRoot) external;
function historicalRoot(uint256 _blockNumber) external view returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {Merkle} from "./Merkle.sol";
import {Arrays} from "@openzeppelin/contracts-v4/utils/Arrays.sol";
/**
* @dev Library for managing https://wikipedia.org/wiki/Merkle_Tree[Merkle Tree] data structures.
*
* Each tree is a complete binary tree with the ability to sequentially insert leaves, changing them from a zero to a
* non-zero value and updating its root. This structure allows inserting commitments (or other entries) that are not
* stored, but can be proven to be part of the tree at a later time if the root is kept. See {MerkleProof}.
*
* A tree is defined by the following parameters:
*
* * Depth: The number of levels in the tree, it also defines the maximum number of leaves as 2**depth.
* * Zero value: The value that represents an empty leaf. Used to avoid regular zero values to be part of the tree.
* * Hashing function: A cryptographic hash function used to produce internal nodes.
*
* This is a fork of OpenZeppelin's [`MerkleTree`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/9af280dc4b45ee5bda96ba47ff829b407eaab67e/contracts/utils/structs/MerkleTree.sol)
* library, with the changes to support dynamic tree growth (doubling the size when full).
*/
library DynamicIncrementalMerkle {
/**
* @dev A complete `bytes32` Merkle tree.
*
* The `sides` and `zero` arrays are set to have a length equal to the depth of the tree during setup.
*
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior.
*
* NOTE: The `root` and the updates history is not stored within the tree. Consider using a secondary structure to
* store a list of historical roots from the values returned from {setup} and {push} (e.g. a mapping, {BitMaps} or
* {Checkpoints}).
*
* WARNING: Updating any of the tree's parameters after the first insertion will result in a corrupted tree.
*/
struct Bytes32PushTree {
uint256 _nextLeafIndex;
bytes32[] _sides;
bytes32[] _zeros;
}
/**
* @dev Initialize a {Bytes32PushTree} using {Hashes-Keccak256} to hash internal nodes.
* The capacity of the tree (i.e. number of leaves) is set to `2**levels`.
*
* IMPORTANT: The zero value should be carefully chosen since it will be stored in the tree representing
* empty leaves. It should be a value that is not expected to be part of the tree.
*/
function setup(Bytes32PushTree storage self, bytes32 zero) internal returns (bytes32 initialRoot) {
self._nextLeafIndex = 0;
self._zeros.push(zero);
self._sides.push(bytes32(0));
return bytes32(0);
}
/**
* @dev Resets the tree to a blank state.
* Calling this function on MerkleTree that was already setup and used will reset it to a blank state.
* @param zero The value that represents an empty leaf.
* @return initialRoot The initial root of the tree.
*/
function reset(Bytes32PushTree storage self, bytes32 zero) internal returns (bytes32 initialRoot) {
self._nextLeafIndex = 0;
uint256 length = self._zeros.length;
for (uint256 i = length; 0 < i; --i) {
self._zeros.pop();
}
length = self._sides.length;
for (uint256 i = length; 0 < i; --i) {
self._sides.pop();
}
self._zeros.push(zero);
self._sides.push(bytes32(0));
return bytes32(0);
}
/**
* @dev Insert a new leaf in the tree, and compute the new root. Returns the position of the inserted leaf in the
* tree, and the resulting root.
*
* Hashing the leaf before calling this function is recommended as a protection against
* second pre-image attacks.
*/
function push(Bytes32PushTree storage self, bytes32 leaf) internal returns (uint256 index, bytes32 newRoot) {
// Cache read
uint256 levels = self._zeros.length - 1;
// Get leaf index
// solhint-disable-next-line gas-increment-by-one
index = self._nextLeafIndex++;
// Check if tree is full.
if (index == 1 << levels) {
bytes32 zero = self._zeros[levels];
bytes32 newZero = Merkle.efficientHash(zero, zero);
self._zeros.push(newZero);
self._sides.push(bytes32(0));
++levels;
}
// Rebuild branch from leaf to root
uint256 currentIndex = index;
bytes32 currentLevelHash = leaf;
bool updatedSides = false;
for (uint32 i = 0; i < levels; ++i) {
// Reaching the parent node, is currentLevelHash the left child?
bool isLeft = currentIndex % 2 == 0;
// If so, next time we will come from the right, so we need to save it
if (isLeft && !updatedSides) {
Arrays.unsafeAccess(self._sides, i).value = currentLevelHash;
updatedSides = true;
}
// Compute the current node hash by using the hash function
// with either its sibling (side) or the zero value for that level.
currentLevelHash = Merkle.efficientHash(
isLeft ? currentLevelHash : Arrays.unsafeAccess(self._sides, i).value,
isLeft ? Arrays.unsafeAccess(self._zeros, i).value : currentLevelHash
);
// Update node index
currentIndex >>= 1;
}
Arrays.unsafeAccess(self._sides, levels).value = currentLevelHash;
return (index, currentLevelHash);
}
/**
* @dev Tree's root.
*/
function root(Bytes32PushTree storage self) internal view returns (bytes32) {
return Arrays.unsafeAccess(self._sides, self._sides.length - 1).value;
}
/**
* @dev Tree's height (does not include the root node).
*/
function height(Bytes32PushTree storage self) internal view returns (uint256) {
return self._sides.length - 1;
}
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {UncheckedMath} from "../../common/libraries/UncheckedMath.sol";
import {MerkleIndexOrHeightMismatch, MerkleIndexOutOfBounds, MerkleNothingToProve, MerklePathEmpty, MerklePathLengthMismatch, MerklePathOutOfBounds} from "../../common/L1ContractErrors.sol";
/// @author Matter Labs
/// @custom:security-contact [email protected]
library Merkle {
using UncheckedMath for uint256;
/// @dev Calculate Merkle root by the provided Merkle proof.
/// NOTE: When using this function, check that the _path length is equal to the tree height to prevent shorter/longer paths attack
/// however, for chains settling on GW the proof includes the GW proof, so the path increases. See Mailbox for more details.
/// @param _path Merkle path from the leaf to the root
/// @param _index Leaf index in the tree
/// @param _itemHash Hash of leaf content
/// @return The Merkle root
function calculateRoot(
bytes32[] calldata _path,
uint256 _index,
bytes32 _itemHash
) internal pure returns (bytes32) {
uint256 pathLength = _path.length;
_validatePathLengthForSingleProof(_index, pathLength);
bytes32 currentHash = _itemHash;
for (uint256 i; i < pathLength; i = i.uncheckedInc()) {
currentHash = (_index % 2 == 0)
? efficientHash(currentHash, _path[i])
: efficientHash(_path[i], currentHash);
_index /= 2;
}
return currentHash;
}
/// @dev Calculate Merkle root by the provided Merkle proof.
/// @dev NOTE: When using this function, check that the _path length is appropriate to prevent shorter/longer paths attack
/// @param _path Merkle path from the leaf to the root
/// @param _index Leaf index in the tree.
/// @dev NOTE the tree can be joined. In this case the second tree's leaves indexes increase by the number of leaves in the first tree.
/// @param _itemHash Hash of leaf content
/// @return The Merkle root
function calculateRootMemory(
bytes32[] memory _path,
uint256 _index,
bytes32 _itemHash
) internal pure returns (bytes32) {
uint256 pathLength = _path.length;
_validatePathLengthForSingleProof(_index, pathLength);
bytes32 currentHash = _itemHash;
for (uint256 i; i < pathLength; i = i.uncheckedInc()) {
currentHash = (_index % 2 == 0)
? efficientHash(currentHash, _path[i])
: efficientHash(_path[i], currentHash);
_index /= 2;
}
return currentHash;
}
/// @dev Calculate Merkle root by the provided Merkle proof for a range of elements
/// NOTE: When using this function, check that the _startPath and _endPath lengths are equal to the tree height to prevent shorter/longer paths attack
/// @param _startPath Merkle path from the first element of the range to the root
/// @param _endPath Merkle path from the last element of the range to the root
/// @param _startIndex Index of the first element of the range in the tree
/// @param _itemHashes Hashes of the elements in the range
/// @return The Merkle root
function calculateRootPaths(
bytes32[] memory _startPath,
bytes32[] memory _endPath,
uint256 _startIndex,
bytes32[] memory _itemHashes
) internal pure returns (bytes32) {
uint256 pathLength = _startPath.length;
if (pathLength != _endPath.length) {
revert MerklePathLengthMismatch(pathLength, _endPath.length);
}
if (pathLength >= 256) {
revert MerklePathOutOfBounds();
}
uint256 levelLen = _itemHashes.length;
// Edge case: we want to be able to prove an element in a single-node tree.
if (pathLength == 0 && (_startIndex != 0 || levelLen != 1)) {
revert MerklePathEmpty();
}
if (levelLen == 0) {
revert MerkleNothingToProve();
}
if (_startIndex + levelLen > (1 << pathLength)) {
revert MerkleIndexOrHeightMismatch();
}
bytes32[] memory itemHashes = _itemHashes;
for (uint256 level; level < pathLength; level = level.uncheckedInc()) {
uint256 parity = _startIndex % 2;
// We get an extra element on the next level if on the current level elements either
// start on an odd index (`parity == 1`) or end on an even index (`levelLen % 2 == 1`)
uint256 nextLevelLen = levelLen / 2 + (parity | (levelLen % 2));
for (uint256 i; i < nextLevelLen; i = i.uncheckedInc()) {
bytes32 lhs = (i == 0 && parity == 1) ? _startPath[level] : itemHashes[2 * i - parity];
bytes32 rhs = (i == nextLevelLen - 1 && (levelLen - parity) % 2 == 1)
? _endPath[level]
: itemHashes[2 * i + 1 - parity];
itemHashes[i] = efficientHash(lhs, rhs);
}
levelLen = nextLevelLen;
_startIndex /= 2;
}
return itemHashes[0];
}
/// @dev Keccak hash of the concatenation of two 32-byte words
function efficientHash(bytes32 _lhs, bytes32 _rhs) internal pure returns (bytes32 result) {
assembly {
mstore(0x00, _lhs)
mstore(0x20, _rhs)
result := keccak256(0x00, 0x40)
}
}
function _validatePathLengthForSingleProof(uint256 _index, uint256 _pathLength) private pure {
if (_pathLength >= 256) {
revert MerklePathOutOfBounds();
}
if (_index >= (1 << _pathLength)) {
revert MerkleIndexOutOfBounds();
}
}
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
/// @notice Part of the configuration parameters of ZKP circuits
struct VerifierParams {
bytes32 recursionNodeLevelVkHash;
bytes32 recursionLeafLevelVkHash;
bytes32 recursionCircuitsSetVksHash;
}
/// @title The interface of the Verifier contract, responsible for the zero knowledge proof verification.
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IVerifier {
/// @dev Verifies a zk-SNARK proof.
/// @return A boolean value indicating whether the zk-SNARK proof is valid.
/// Note: The function may revert execution instead of returning false in some cases.
function verify(uint256[] calldata _publicInputs, uint256[] calldata _proof) external view returns (bool);
/// @notice Calculates a keccak256 hash of the runtime loaded verification keys.
/// @return vkHash The keccak256 hash of the loaded verification keys.
function verificationKeyHash() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {QueueIsEmpty} from "../../common/L1ContractErrors.sol";
/// @notice The structure that contains meta information of the L2 transaction that was requested from L1
/// @dev The weird size of fields was selected specifically to minimize the structure storage size
/// @param canonicalTxHash Hashed L2 transaction data that is needed to process it
/// @param expirationTimestamp Expiration timestamp for this request (must be satisfied before)
/// @param layer2Tip Additional payment to the validator as an incentive to perform the operation
struct PriorityOperation {
bytes32 canonicalTxHash;
uint64 expirationTimestamp;
uint192 layer2Tip;
}
/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @dev The library provides the API to interact with the priority queue container
/// @dev Order of processing operations from queue - FIFO (Fist in - first out)
library PriorityQueue {
using PriorityQueue for Queue;
/// @notice Container that stores priority operations
/// @param data The inner mapping that saves priority operation by its index
/// @param head The pointer to the first unprocessed priority operation, equal to the tail if the queue is empty
/// @param tail The pointer to the free slot
struct Queue {
mapping(uint256 priorityOpId => PriorityOperation priorityOp) data;
uint256 tail;
uint256 head;
}
/// @notice Returns zero if and only if no operations were processed from the queue
/// @return Index of the oldest priority operation that wasn't processed yet
function getFirstUnprocessedPriorityTx(Queue storage _queue) internal view returns (uint256) {
return _queue.head;
}
/// @return The total number of priority operations that were added to the priority queue, including all processed ones
function getTotalPriorityTxs(Queue storage _queue) internal view returns (uint256) {
return _queue.tail;
}
/// @return The total number of unprocessed priority operations in a priority queue
function getSize(Queue storage _queue) internal view returns (uint256) {
return uint256(_queue.tail - _queue.head);
}
/// @return Whether the priority queue contains no operations
function isEmpty(Queue storage _queue) internal view returns (bool) {
return _queue.tail == _queue.head;
}
/// @notice Add the priority operation to the end of the priority queue
function pushBack(Queue storage _queue, PriorityOperation memory _operation) internal {
// Save value into the stack to avoid double reading from the storage
uint256 tail = _queue.tail;
_queue.data[tail] = _operation;
_queue.tail = tail + 1;
}
/// @return The first unprocessed priority operation from the queue
function front(Queue storage _queue) internal view returns (PriorityOperation memory) {
// priority queue is empty
if (_queue.isEmpty()) {
revert QueueIsEmpty();
}
return _queue.data[_queue.head];
}
/// @notice Remove the first unprocessed priority operation from the queue
/// @return priorityOperation that was popped from the priority queue
function popFront(Queue storage _queue) internal returns (PriorityOperation memory priorityOperation) {
// priority queue is empty
if (_queue.isEmpty()) {
revert QueueIsEmpty();
}
// Save value into the stack to avoid double reading from the storage
uint256 head = _queue.head;
priorityOperation = _queue.data[head];
delete _queue.data[head];
_queue.head = head + 1;
}
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {NotInitializedReentrancyGuard, Reentrancy, SlotOccupied} from "./L1ContractErrors.sol";
/**
* @custom:security-contact [email protected]
* @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.
*/
abstract contract ReentrancyGuard {
/// @dev Address of lock flag variable.
/// @dev Flag is placed at random memory location to not interfere with Storage contract.
// keccak256("ReentrancyGuard") - 1;
uint256 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4;
// solhint-disable-next-line max-line-length
// 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;
modifier reentrancyGuardInitializer() {
_initializeReentrancyGuard();
_;
}
function _initializeReentrancyGuard() private {
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
if (lockSlotOldValue != 0) {
revert SlotOccupied();
}
}
/**
* @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)
}
if (_status == 0) {
revert NotInitializedReentrancyGuard();
}
// On the first call to nonReentrant, _NOT_ENTERED will be true
if (_status != _NOT_ENTERED) {
revert Reentrancy();
}
// 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)
}
}
}// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.20;
/// @dev The log passed from L2
/// @param l2ShardId The shard identifier, 0 - rollup, 1 - porter. All other values are not used but are reserved for the future
/// @param isService A boolean flag that is part of the log along with `key`, `value`, and `sender` address.
/// This field is required formally but does not have any special meaning.
/// @param txNumberInBlock The L2 transaction number in a block, in which the log was sent
/// @param sender The L2 address which sent the log
/// @param key The 32 bytes of information that was sent in the log
/// @param value The 32 bytes of information that was sent in the log
// Both `key` and `value` are arbitrary 32-bytes selected by the log sender
struct L2ToL1Log {
uint8 l2ShardId;
bool isService;
uint16 txNumberInBlock;
address sender;
bytes32 key;
bytes32 value;
}
/// @dev Bytes in raw L2 to L1 log
/// @dev Equal to the bytes size of the tuple - (uint8 ShardId, bool isService, uint16 txNumberInBlock, address sender, bytes32 key, bytes32 value)
uint256 constant L2_TO_L1_LOG_SERIALIZE_SIZE = 88;
/// @dev The value of default leaf hash for L2 to L1 logs Merkle tree
/// @dev An incomplete fixed-size tree is filled with this value to be a full binary tree
/// @dev Actually equal to the `keccak256(new bytes(L2_TO_L1_LOG_SERIALIZE_SIZE))`
bytes32 constant L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH = 0x72abee45b59e344af8a6e520241c4744aff26ed411f4c4b00f8af09adada43ba;
/// @dev The current version of state diff compression being used.
uint256 constant STATE_DIFF_COMPRESSION_VERSION_NUMBER = 1;
/**
* @author Matter Labs
* @custom:security-contact [email protected]
* @notice The interface of the L1 Messenger contract, responsible for sending messages to L1.
* @dev by default ZkSync can send fixed-length messages on L1.
* A fixed length message has 4 parameters `senderAddress`, `isService`, `key`, `value`,
* the first one is taken from the context, the other three are chosen by the sender.
* @dev To send a variable-length message we use this trick:
* - This system contract accepts an arbitrary length message and sends a fixed length message with
* parameters `senderAddress == this`, `isService == true`, `key == msg.sender`, `value == keccak256(message)`.
* - The contract on L1 accepts all sent messages and if the message came from this system contract
* it requires the preimage of `value` to be provided.
*/
interface IL2ToL1Messenger {
// Possibly in the future we will be able to track the messages sent to L1 with
// some hooks in the VM. For now, it is much easier to track them with L2 events.
event L1MessageSent(address indexed _sender, bytes32 indexed _hash, bytes _message);
/// @notice Sends an arbitrary length message to L1.
/// @param _message The variable length message to be sent to L1.
/// @return Returns the keccak256 hashed value of the message.
function sendToL1(bytes calldata _message) external returns (bytes32);
/// @notice Sends L2ToL1Log.
/// @param _isService The `isService` flag.
/// @param _key The `key` part of the L2Log.
/// @param _value The `value` part of the L2Log.
/// @dev Can be called only by a system contract.
function sendL2ToL1Log(bool _isService, bytes32 _key, bytes32 _value) external returns (uint256 logIdInMerkleTree);
/// @notice This function is expected to be called only by the KnownCodesStorage system contract
function requestBytecodeL1Publication(bytes32 _bytecodeHash) external;
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /** * @author Matter Labs * @custom:security-contact [email protected] * @notice The interface of the L2 InteropRootStorage contract, * responsible for storing the message roots of other chains on the L2. */ interface IL2InteropRootStorage { /// @notice Mapping of chain ID to block or batch number to message root. function interopRoots(uint256 chainId, uint256 blockOrBatchNumber) external view returns (bytes32); }
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IBridgehub} from "../../bridgehub/IBridgehub.sol";
import {IL1NativeTokenVault} from "../ntv/IL1NativeTokenVault.sol";
import {IL1ERC20Bridge} from "./IL1ERC20Bridge.sol";
/// @param chainId The chain ID of the transaction to check.
/// @param l2BatchNumber The L2 batch number where the withdrawal was processed.
/// @param l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
/// @param l2sender The address of the message sender on L2 (base token system contract address or asset handler)
/// @param l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent.
/// @param message The L2 withdraw data, stored in an L2 -> L1 message.
/// @param merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization.
struct FinalizeL1DepositParams {
uint256 chainId;
uint256 l2BatchNumber;
uint256 l2MessageIndex;
address l2Sender;
uint16 l2TxNumberInBatch;
bytes message;
bytes32[] merkleProof;
}
/// @title L1 Bridge contract interface
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IL1Nullifier {
event BridgehubDepositFinalized(
uint256 indexed chainId,
bytes32 indexed txDataHash,
bytes32 indexed l2DepositTxHash
);
function isWithdrawalFinalized(
uint256 _chainId,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex
) external view returns (bool);
function claimFailedDepositLegacyErc20Bridge(
address _depositSender,
address _l1Token,
uint256 _amount,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external;
function claimFailedDeposit(
uint256 _chainId,
address _depositSender,
address _l1Token,
uint256 _amount,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external;
function finalizeDeposit(FinalizeL1DepositParams calldata _finalizeWithdrawalParams) external;
function BRIDGE_HUB() external view returns (IBridgehub);
function legacyBridge() external view returns (IL1ERC20Bridge);
function depositHappened(uint256 _chainId, bytes32 _l2TxHash) external view returns (bytes32);
function bridgehubConfirmL2TransactionForwarded(uint256 _chainId, bytes32 _txDataHash, bytes32 _txHash) external;
function l1NativeTokenVault() external view returns (IL1NativeTokenVault);
function setL1NativeTokenVault(IL1NativeTokenVault _nativeTokenVault) external;
function setL1AssetRouter(address _l1AssetRouter) external;
function chainBalance(uint256 _chainId, address _token) external view returns (uint256);
function l2BridgeAddress(uint256 _chainId) external view returns (address);
function transferTokenToNTV(address _token) external;
function nullifyChainBalanceByNTV(uint256 _chainId, address _token) external;
/// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2.
/// @param _chainId The ZK chain id to which deposit was initiated.
/// @param _depositSender The address of the entity that initiated the deposit.
/// @param _assetId The unique identifier of the deposited L1 token.
/// @param _assetData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. Might include extra information.
/// @param _l2TxHash The L2 transaction hash of the failed deposit finalization.
/// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed.
/// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
/// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent.
/// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization.
/// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system.
function bridgeRecoverFailedTransfer(
uint256 _chainId,
address _depositSender,
bytes32 _assetId,
bytes memory _assetData,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external;
/// @notice Legacy function to finalize withdrawal via the same
/// interface as the old L1SharedBridge.
/// @dev Note, that we need to keep this interface, since the `L2AssetRouter`
/// will continue returning the previous address as the `l1SharedBridge`. The value
/// returned by it is used in the SDK for finalizing withdrawals.
/// @param _chainId The chain ID of the transaction to check
/// @param _l2BatchNumber The L2 batch number where the withdrawal was processed
/// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message
/// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent
/// @param _message The L2 withdraw data, stored in an L2 -> L1 message
/// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization
function finalizeWithdrawal(
uint256 _chainId,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes calldata _message,
bytes32[] calldata _merkleProof
) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IAssetRouterBase} from "../asset-router/IAssetRouterBase.sol";
/// @title Base Native token vault contract interface
/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @notice The NTV is an Asset Handler for the L1AssetRouter to handle native tokens
interface INativeTokenVault {
event BridgedTokenBeaconUpdated(address bridgedTokenBeacon, bytes32 bridgedTokenProxyBytecodeHash);
/// @notice The Weth token address
function WETH_TOKEN() external view returns (address);
/// @notice The AssetRouter contract
function ASSET_ROUTER() external view returns (IAssetRouterBase);
/// @notice The chain ID of the L1 chain
function L1_CHAIN_ID() external view returns (uint256);
/// @notice Returns the chain ID of the origin chain for a given asset ID
function originChainId(bytes32 assetId) external view returns (uint256);
/// @notice Registers tokens within the NTV.
/// @dev The goal is to allow bridging native tokens automatically, by registering them on the fly.
/// @notice Allows the bridge to register a token address for the vault.
/// @notice No access control is ok, since the bridging of tokens should be permissionless. This requires permissionless registration.
function registerToken(address _l1Token) external;
/// @notice Ensures that the native token is registered with the NTV.
/// @dev This function is used to ensure that the token is registered with the NTV.
function ensureTokenIsRegistered(address _nativeToken) external returns (bytes32);
/// @notice Used to get the the ERC20 data for a token
function getERC20Getters(address _token, uint256 _originChainId) external view returns (bytes memory);
/// @notice Used to get the token address of an assetId
function tokenAddress(bytes32 assetId) external view returns (address);
/// @notice Used to get the assetId of a token
function assetId(address token) external view returns (bytes32);
/// @notice Used to get the expected bridged token address corresponding to its native counterpart
function calculateCreate2TokenAddress(uint256 _originChainId, address _originToken) external view returns (address);
/// @notice Tries to register a token from the provided `_burnData` and reverts if it is not possible.
function tryRegisterTokenFromBurnData(bytes calldata _burnData, bytes32 _expectedAssetId) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IBridgehub} from "../../bridgehub/IBridgehub.sol";
/// @dev The encoding version used for legacy txs.
bytes1 constant LEGACY_ENCODING_VERSION = 0x00;
/// @dev The encoding version used for new txs.
bytes1 constant NEW_ENCODING_VERSION = 0x01;
/// @dev The encoding version used for txs that set the asset handler on the counterpart contract.
bytes1 constant SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION = 0x02;
/// @title L1 Bridge contract interface
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IAssetRouterBase {
event BridgehubDepositBaseTokenInitiated(
uint256 indexed chainId,
address indexed from,
bytes32 assetId,
uint256 amount
);
event BridgehubDepositInitiated(
uint256 indexed chainId,
bytes32 indexed txDataHash,
address indexed from,
bytes32 assetId,
bytes bridgeMintCalldata
);
event BridgehubWithdrawalInitiated(
uint256 chainId,
address indexed sender,
bytes32 indexed assetId,
bytes32 assetDataHash // Todo: What's the point of emitting hash?
);
event AssetDeploymentTrackerRegistered(
bytes32 indexed assetId,
bytes32 indexed additionalData,
address assetDeploymentTracker
);
event AssetHandlerRegistered(bytes32 indexed assetId, address indexed _assetHandlerAddress);
event DepositFinalizedAssetRouter(uint256 indexed chainId, bytes32 indexed assetId, bytes assetData);
function BRIDGE_HUB() external view returns (IBridgehub);
function L1_CHAIN_ID() external view returns (uint256);
/// @notice Sets the asset handler address for a specified asset ID on the chain of the asset deployment tracker.
/// @dev The caller of this function is encoded within the `assetId`, therefore, it should be invoked by the asset deployment tracker contract.
/// @dev No access control on the caller, as msg.sender is encoded in the assetId.
/// @dev Typically, for most tokens, ADT is the native token vault. However, custom tokens may have their own specific asset deployment trackers.
/// @dev `setAssetHandlerAddressOnCounterpart` should be called on L1 to set asset handlers on L2 chains for a specific asset ID.
/// @param _assetRegistrationData The asset data which may include the asset address and any additional required data or encodings.
/// @param _assetHandlerAddress The address of the asset handler to be set for the provided asset.
function setAssetHandlerAddressThisChain(bytes32 _assetRegistrationData, address _assetHandlerAddress) external;
function assetHandlerAddress(bytes32 _assetId) external view returns (address);
/// @notice Finalize the withdrawal and release funds.
/// @param _chainId The chain ID of the transaction to check.
/// @param _assetId The bridged asset ID.
/// @param _transferData The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
/// @dev We have both the legacy finalizeWithdrawal and the new finalizeDeposit functions,
/// finalizeDeposit uses the new format. On the L2 we have finalizeDeposit with new and old formats both.
function finalizeDeposit(uint256 _chainId, bytes32 _assetId, bytes memory _transferData) external payable;
}// SPDX-License-Identifier: MIT pragma solidity 0.8.28; /// @title L1 Bridge contract interface /// @author Matter Labs /// @custom:security-contact [email protected] interface IL1SharedBridgeLegacy { function l2BridgeAddress(uint256 _chainId) external view returns (address); }
// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;
import {IL1Nullifier} from "./IL1Nullifier.sol";
import {IL1NativeTokenVault} from "../ntv/IL1NativeTokenVault.sol";
import {IL1AssetRouter} from "../asset-router/IL1AssetRouter.sol";
/// @title L1 Bridge contract legacy interface
/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @notice Legacy Bridge interface before ZK chain migration, used for backward compatibility with ZKsync Era
interface IL1ERC20Bridge {
event DepositInitiated(
bytes32 indexed l2DepositTxHash,
address indexed from,
address indexed to,
address l1Token,
uint256 amount
);
event WithdrawalFinalized(address indexed to, address indexed l1Token, uint256 amount);
event ClaimedFailedDeposit(address indexed to, address indexed l1Token, uint256 amount);
function isWithdrawalFinalized(uint256 _l2BatchNumber, uint256 _l2MessageIndex) external view returns (bool);
function deposit(
address _l2Receiver,
address _l1Token,
uint256 _amount,
uint256 _l2TxGasLimit,
uint256 _l2TxGasPerPubdataByte,
address _refundRecipient
) external payable returns (bytes32 txHash);
function deposit(
address _l2Receiver,
address _l1Token,
uint256 _amount,
uint256 _l2TxGasLimit,
uint256 _l2TxGasPerPubdataByte
) external payable returns (bytes32 txHash);
function claimFailedDeposit(
address _depositSender,
address _l1Token,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external;
function finalizeWithdrawal(
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes calldata _message,
bytes32[] calldata _merkleProof
) external;
function l2TokenAddress(address _l1Token) external view returns (address);
function L1_NULLIFIER() external view returns (IL1Nullifier);
function L1_ASSET_ROUTER() external view returns (IL1AssetRouter);
function L1_NATIVE_TOKEN_VAULT() external view returns (IL1NativeTokenVault);
function l2TokenBeacon() external view returns (address);
function l2Bridge() external view returns (address);
function depositAmount(
address _account,
address _l1Token,
bytes32 _depositL2TxHash
) external view returns (uint256 amount);
}// SPDX-License-Identifier: MIT pragma solidity 0.8.28; /// @author Matter Labs /// @custom:security-contact [email protected] interface IL1AssetDeploymentTracker { function bridgeCheckCounterpartAddress( uint256 _chainId, bytes32 _assetId, address _originalCaller, address _assetHandlerAddressOnCounterpart ) external view; }
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Arrays.sol)
pragma solidity ^0.8.0;
import "./StorageSlot.sol";
import "./math/Math.sol";
/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using StorageSlot for bytes32;
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order, and to contain no
* repeated elements.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
if (array.length == 0) {
return 0;
}
uint256 low = 0;
uint256 high = array.length;
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds down (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getAddressSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getBytes32Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getUint256Slot();
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol";
import {INativeTokenVault} from "./INativeTokenVault.sol";
import {IL1AssetDeploymentTracker} from "../interfaces/IL1AssetDeploymentTracker.sol";
/// @title L1 Native token vault contract interface
/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @notice The NTV is an Asset Handler for the L1AssetRouter to handle native tokens
// is IL1AssetHandler, IL1BaseTokenAssetHandler {
interface IL1NativeTokenVault is INativeTokenVault, IL1AssetDeploymentTracker {
/// @notice The L1Nullifier contract
function L1_NULLIFIER() external view returns (IL1Nullifier);
/// @notice Returns the total number of specific tokens locked for some chain
function chainBalance(uint256 _chainId, bytes32 _assetId) external view returns (uint256);
/// @notice Registers ETH token
function registerEthToken() external;
event TokenBeaconUpdated(address indexed l2TokenBeacon);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}{
"remappings": [
"@ensdomains/=node_modules/@ensdomains/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"forge-std/=lib/forge-std/src/",
"hardhat/=node_modules/hardhat/",
"murky/=lib/murky/src/",
"foundry-test/=test/foundry/",
"l2-contracts/=../l2-contracts/contracts/",
"@openzeppelin/contracts-v4/=lib/openzeppelin-contracts-v4/contracts/",
"@openzeppelin/contracts-upgradeable-v4/=lib/openzeppelin-contracts-upgradeable-v4/contracts/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable-v4/lib/erc4626-tests/",
"openzeppelin-contracts-upgradeable-v4/=lib/openzeppelin-contracts-upgradeable-v4/",
"openzeppelin-contracts-v4/=lib/openzeppelin-contracts-v4/",
"openzeppelin-contracts/=lib/murky/lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 9999999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false
}Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"_eraChainId","type":"uint256"},{"internalType":"uint256","name":"_l1ChainId","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BaseTokenGasPriceDenominatorNotSet","type":"error"},{"inputs":[{"internalType":"uint256","name":"batchNumber","type":"uint256"}],"name":"BatchNotExecuted","type":"error"},{"inputs":[],"name":"GasPerPubdataMismatch","type":"error"},{"inputs":[],"name":"HashedLogIsDefault","type":"error"},{"inputs":[],"name":"InvalidChainId","type":"error"},{"inputs":[],"name":"InvalidProofLengthForFinalNode","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"LengthIsNotDivisibleBy32","type":"error"},{"inputs":[],"name":"LocalRootIsZero","type":"error"},{"inputs":[],"name":"LocalRootMustBeZero","type":"error"},{"inputs":[{"internalType":"enum BytecodeError","name":"","type":"uint8"}],"name":"MalformedBytecode","type":"error"},{"inputs":[],"name":"MerkleIndexOutOfBounds","type":"error"},{"inputs":[],"name":"MerklePathEmpty","type":"error"},{"inputs":[],"name":"MerklePathOutOfBounds","type":"error"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"},{"internalType":"uint256","name":"provided","type":"uint256"}],"name":"MsgValueTooLow","type":"error"},{"inputs":[],"name":"NotHyperchain","type":"error"},{"inputs":[],"name":"NotInitializedReentrancyGuard","type":"error"},{"inputs":[{"internalType":"uint256","name":"blockChainId","type":"uint256"}],"name":"NotL1","type":"error"},{"inputs":[],"name":"NotSettlementLayer","type":"error"},{"inputs":[],"name":"OnlyEraSupported","type":"error"},{"inputs":[{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"PubdataGreaterThanLimit","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"TooManyFactoryDeps","type":"error"},{"inputs":[],"name":"TooMuchGas","type":"error"},{"inputs":[],"name":"TransactionNotAllowed","type":"error"},{"inputs":[],"name":"TxnBodyGasLimitNotEnoughGas","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"uint256","name":"metadataVersion","type":"uint256"}],"name":"UnsupportedProofMetadataVersion","type":"error"},{"inputs":[],"name":"ValidateTxnNotEnoughGas","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"txId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"expirationTimestamp","type":"uint64"},{"components":[{"internalType":"uint256","name":"txType","type":"uint256"},{"internalType":"uint256","name":"from","type":"uint256"},{"internalType":"uint256","name":"to","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"gasPerPubdataByteLimit","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"uint256","name":"paymaster","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256[4]","name":"reserved","type":"uint256[4]"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256[]","name":"factoryDeps","type":"uint256[]"},{"internalType":"bytes","name":"paymasterInput","type":"bytes"},{"internalType":"bytes","name":"reservedDynamic","type":"bytes"}],"indexed":false,"internalType":"struct L2CanonicalTransaction","name":"transaction","type":"tuple"},{"indexed":false,"internalType":"bytes[]","name":"factoryDeps","type":"bytes[]"}],"name":"NewPriorityRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"txId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"}],"name":"NewPriorityRequestId","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"txId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"expirationTimestamp","type":"uint64"}],"name":"NewRelayedPriorityTransaction","type":"event"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"contractL2","type":"address"},{"internalType":"uint256","name":"mintValue","type":"uint256"},{"internalType":"uint256","name":"l2Value","type":"uint256"},{"internalType":"bytes","name":"l2Calldata","type":"bytes"},{"internalType":"uint256","name":"l2GasLimit","type":"uint256"},{"internalType":"uint256","name":"l2GasPerPubdataByteLimit","type":"uint256"},{"internalType":"bytes[]","name":"factoryDeps","type":"bytes[]"},{"internalType":"address","name":"refundRecipient","type":"address"}],"internalType":"struct BridgehubL2TransactionRequest","name":"_request","type":"tuple"}],"name":"bridgehubRequestL2Transaction","outputs":[{"internalType":"bytes32","name":"canonicalTxHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_canonicalTxHash","type":"bytes32"},{"internalType":"uint64","name":"_expirationTimestamp","type":"uint64"}],"name":"bridgehubRequestL2TransactionOnGateway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_l2BatchNumber","type":"uint256"},{"internalType":"uint256","name":"_l2MessageIndex","type":"uint256"},{"internalType":"uint16","name":"_l2TxNumberInBatch","type":"uint16"},{"internalType":"bytes","name":"_message","type":"bytes"},{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"}],"name":"finalizeEthWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasPrice","type":"uint256"},{"internalType":"uint256","name":"_l2GasLimit","type":"uint256"},{"internalType":"uint256","name":"_l2GasPerPubdataByteLimit","type":"uint256"}],"name":"l2TransactionBaseCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_l2TxHash","type":"bytes32"},{"internalType":"uint256","name":"_l2BatchNumber","type":"uint256"},{"internalType":"uint256","name":"_l2MessageIndex","type":"uint256"},{"internalType":"uint16","name":"_l2TxNumberInBatch","type":"uint16"},{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"},{"internalType":"enum TxStatus","name":"_status","type":"uint8"}],"name":"proveL1ToL2TransactionStatus","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_batchNumber","type":"uint256"},{"internalType":"uint256","name":"_leafProofMask","type":"uint256"},{"internalType":"bytes32","name":"_leaf","type":"bytes32"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"proveL2LeafInclusion","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"uint256","name":"_blockOrBatchNumber","type":"uint256"},{"internalType":"uint256","name":"_leafProofMask","type":"uint256"},{"internalType":"bytes32","name":"_leaf","type":"bytes32"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"proveL2LeafInclusionShared","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_batchNumber","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"},{"components":[{"internalType":"uint8","name":"l2ShardId","type":"uint8"},{"internalType":"bool","name":"isService","type":"bool"},{"internalType":"uint16","name":"txNumberInBatch","type":"uint16"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32","name":"value","type":"bytes32"}],"internalType":"struct L2Log","name":"_log","type":"tuple"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"proveL2LogInclusion","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"uint256","name":"_blockOrBatchNumber","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"},{"components":[{"internalType":"uint8","name":"l2ShardId","type":"uint8"},{"internalType":"bool","name":"isService","type":"bool"},{"internalType":"uint16","name":"txNumberInBatch","type":"uint16"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32","name":"value","type":"bytes32"}],"internalType":"struct L2Log","name":"_log","type":"tuple"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"proveL2LogInclusionShared","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_batchNumber","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"},{"components":[{"internalType":"uint16","name":"txNumberInBatch","type":"uint16"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct L2Message","name":"_message","type":"tuple"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"proveL2MessageInclusion","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"uint256","name":"_blockOrBatchNumber","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"},{"components":[{"internalType":"uint16","name":"txNumberInBatch","type":"uint16"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct L2Message","name":"_message","type":"tuple"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"proveL2MessageInclusionShared","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contractL2","type":"address"},{"internalType":"bytes","name":"_l2Calldata","type":"bytes"}],"name":"requestL2ServiceTransaction","outputs":[{"internalType":"bytes32","name":"canonicalTxHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contractL2","type":"address"},{"internalType":"uint256","name":"_l2Value","type":"uint256"},{"internalType":"bytes","name":"_calldata","type":"bytes"},{"internalType":"uint256","name":"_l2GasLimit","type":"uint256"},{"internalType":"uint256","name":"_l2GasPerPubdataByteLimit","type":"uint256"},{"internalType":"bytes[]","name":"_factoryDeps","type":"bytes[]"},{"internalType":"address","name":"_refundRecipient","type":"address"}],"name":"requestL2Transaction","outputs":[{"internalType":"bytes32","name":"canonicalTxHash","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"bytes32","name":"_canonicalTxHash","type":"bytes32"},{"internalType":"uint64","name":"_expirationTimestamp","type":"uint64"}],"name":"requestL2TransactionToGatewayMailbox","outputs":[{"internalType":"bytes32","name":"canonicalTxHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60c060405234801561000f575f5ffd5b506040516141af3803806141af83398101604081905261002e9161003c565b60809190915260a05261005e565b5f5f6040838503121561004d575f5ffd5b505080516020909101519092909150565b60805160a05161411461009b5f395f818161053c015281816107e40152610d4e01525f818161059a015281816106ce0152610dac01526141145ff3fe6080604052600436106100e4575f3560e01c80637efda2ae11610087578063ddcc9eec11610057578063ddcc9eec14610298578063e4948f43146102b7578063e896760d146102d6578063eb672419146102f5575f5ffd5b80637efda2ae1461021c578063b473318e1461023b578063d07725511461025a578063d07b90d114610279575f5ffd5b806318b7fc22116100c257806318b7fc221461019e578063263b7f8e146101bd5780636c0960f9146101dc57806379cf6165146101fd575f5ffd5b8063042901c7146100e857806312f43dab1461011c57806317d7de7c14610149575b5f5ffd5b3480156100f3575f5ffd5b506101076101023660046130dc565b610308565b60405190151581526020015b60405180910390f35b348015610127575f5ffd5b5061013b61013636600461315e565b610396565b604051908152602001610113565b348015610154575f5ffd5b506101916040518060400160405280600c81526020017f4d61696c626f784661636574000000000000000000000000000000000000000081525081565b60405161011391906131e2565b3480156101a9575f5ffd5b506101076101b836600461320a565b610407565b3480156101c8575f5ffd5b506101076101d73660046132a0565b61045f565b3480156101e7575f5ffd5b506101fb6101f6366004613344565b610480565b005b348015610208575f5ffd5b506101076102173660046133d6565b610760565b348015610227575f5ffd5b5061010761023636600461341d565b6107ab565b348015610246575f5ffd5b5061013b61025536600461345c565b6107be565b348015610265575f5ffd5b5061013b61027436600461349c565b6107e1565b348015610284575f5ffd5b5061013b6102933660046134fd565b610a02565b3480156102a3575f5ffd5b506101fb6102b236600461354e565b610c09565b3480156102c2575f5ffd5b506101076102d1366004613578565b610cec565b3480156102e1575f5ffd5b506101076102f03660046135d8565b610d00565b61013b610303366004613629565b610d4b565b5f5f6040518060c001604052805f60ff1681526020016001151581526020018761ffff16815260200161800060016103409190613705565b73ffffffffffffffffffffffffffffffffffffffff1681526020018a815260200184600181111561037357610373613731565b9052602854909150610389908989848989611006565b9998505050505050505050565b6029545f9073ffffffffffffffffffffffffffffffffffffffff1633146103f0576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b6104016103fc83613954565b611105565b92915050565b6028545f908714610444576040517f7a47c9a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61045287878787878761134c565b90505b9695505050505050565b6028545f9061045590878761047936899003890189613a2d565b8787611006565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f8190036104dc576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018114610516576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4557f00000000000000000000000000000000000000000000000000000000000000004614610595576040517f87470e360000000000000000000000000000000000000000000000000000000081524660048201526024016103e7565b6028547f0000000000000000000000000000000000000000000000000000000000000000146105f0576040517ff3ed9dfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602954604080517fbc0aac1000000000000000000000000000000000000000000000000000000000815290515f9273ffffffffffffffffffffffffffffffffffffffff169163bc0aac109160048083019260209291908290030181865afa15801561065d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106819190613aad565b6040517fc87325f100000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063c87325f190610704907f0000000000000000000000000000000000000000000000000000000000000000908d908d908d908d908d908d908d90600401613ac8565b5f604051808303815f87803b15801561071b575f5ffd5b505af115801561072d573d5f5f3e3d5ffd5b505050505060017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4555050505050505050565b6028545f90871461079d576040517f7a47c9a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61045287878787878761135c565b5f6104555f602801548787878787611367565b5f5f6107ca8584611657565b90506107d68482613b82565b9150505b9392505050565b5f7f0000000000000000000000000000000000000000000000000000000000000000461461083d576040517f87470e360000000000000000000000000000000000000000000000000000000081524660048201526024016103e7565b6029546028546040517fe9420f8c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092169163e9420f8c916108989160040190815260200190565b602060405180830381865afa1580156108b3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108d79190613b99565b61090d576040517fd0266e2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6029546040517fe680c4c100000000000000000000000000000000000000000000000000000000815260048101869052339173ffffffffffffffffffffffffffffffffffffffff169063e680c4c190602401602060405180830381865afa15801561097a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061099e9190613aad565b73ffffffffffffffffffffffffffffffffffffffff16146109eb576040517f32ddf9a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6109f7858585611883565b90506107d681611a7d565b5f333014610a3e576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016103e7565b610b2260405180610120016040528073ffffffffffffffffffffffffffffffffffffffff801681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f815260200185858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250938552505063044aa2006020840152506103206040830152606090910190604051908082528060200260200182016040528015610b1357816020015b6060815260200190600190039081610afe5790505b5081525f602090910152611a7d565b60325490915073ffffffffffffffffffffffffffffffffffffffff16156107da5760325460285473ffffffffffffffffffffffffffffffffffffffff9091169063d07725519083610b735f42613bb4565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526004810193909352602483019190915267ffffffffffffffff1660448201526064016020604051808303815f875af1158015610bdd573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c019190613bc7565b509392505050565b60295473ffffffffffffffffffffffffffffffffffffffff163314610c5c576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016103e7565b610c668282611bd4565b7f0137d2eaa6ec5b7e4f233f6d6f441410014535d0f3985367994c94bf15a2a564610c8f611be3565b604080519182526020820185905267ffffffffffffffff84169082015260600160405180910390a181610cc0611be3565b6040517f779f441679936c5441b671969f37400b8c3ed0071cb47444431bf985754560df905f90a35050565b5f6104555f60280154878761047988611bf3565b6028545f908714610d3d576040517f7a47c9a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610452878787878787611cd4565b5f7f00000000000000000000000000000000000000000000000000000000000000004614610da7576040517f87470e360000000000000000000000000000000000000000000000000000000081524660048201526024016103e7565b6028547f000000000000000000000000000000000000000000000000000000000000000014610e02576040517ff3ed9dfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ecc6040518061012001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018c73ffffffffffffffffffffffffffffffffffffffff1681526020013481526020018b81526020018a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505050908252506020810189905260408101889052606001610ea88688613bde565b81526020018473ffffffffffffffffffffffffffffffffffffffff16815250611105565b90505f5f6029015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bc0aac106040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f3b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f5f9190613aad565b6028546031546040517fc487944000000000000000000000000000000000000000000000000000000000815260048101929092526024820152336044820152346064820181905291925073ffffffffffffffffffffffffffffffffffffffff83169163c4879440916084015f604051808303818588803b158015610fe1575f5ffd5b505af1158015610ff3573d5f5f3e3d5ffd5b5050505050509998505050505050505050565b5f5f845f015185602001518660400151876060015188608001518960a001516040516020016110d39695949392919060f896871b7fff0000000000000000000000000000000000000000000000000000000000000016815294151590951b600185015260f09290921b7fffff00000000000000000000000000000000000000000000000000000000000016600284015260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660048301526018820152603881019190915260580190565b6040516020818303038152906040528051906020012090506110f9888888848888611367565b98975050505050505050565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f90808203611162576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001811461119c576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf455602e5473ffffffffffffffffffffffffffffffffffffffff16156112c957602e5483516020850151604080870151606088015160808901516101008a015193517fcbcf2e3c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9097169663cbcf2e3c966112559690959094939291600401613bea565b602060405180830381865afa158015611270573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112949190613b99565b6112c9576040517ec5a6a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103208360c0015114611308576040517fc91cf3b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611310612f38565b6060810184905261132081611cea565b60017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf455949350505050565b5f61045287878761047988611bf3565b5f6104528787878787875b5f5f611377888888888888611f5e565b90508060e001511561141557600b548711156113c2576040517f2078a6a0000000000000000000000000000000000000000000000000000000008152600481018890526024016103e7565b5f878152600f602052604090205480611407576040517fa969e48600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b608090910151149050610455565b5f878152600f60205260409020541561145a576040517fbdaf7d4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60295481516040517fe9420f8c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092169163e9420f8c916114b49160040190815260200190565b602060405180830381865afa1580156114cf573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114f39190613b99565b611529576040517fd0266e2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60295481516040517fe680c4c100000000000000000000000000000000000000000000000000000000815260048101919091525f9173ffffffffffffffffffffffffffffffffffffffff169063e680c4c190602401602060405180830381865afa158015611599573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115bd9190613aad565b90508073ffffffffffffffffffffffffffffffffffffffff16637efda2ae836020015184604001518560a001516115f98a8a8960c0015161222d565b6040518563ffffffff1660e01b81526004016116189493929190613c69565b602060405180830381865afa158015611633573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103899190613b99565b6040805160c08101909152602680545f9283929091829060ff16600181111561168257611682613731565b600181111561169357611693613731565b81529054610100810463ffffffff90811660208401526501000000000082048116604084015269010000000000000000008204811660608401526d0100000000000000000000000000820416608083015271010000000000000000000000000000000000900467ffffffffffffffff1660a090910152602d5490915070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff165f0361176d576040517f6ef9a97200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602d545f906fffffffffffffffffffffffffffffffff70010000000000000000000000000000000082048116916117a5911687613b82565b6117af9190613cf1565b90505f80835160018111156117c6576117c6613731565b036117d9576117d6826011613b82565b90505b5f82846020015163ffffffff166117f09190613b82565b90505f846040015163ffffffff16826118099190613cf1565b6118139084613bb4565b90505f856060015163ffffffff168361182c9190613cf1565b8660a0015167ffffffffffffffff166118459190613bb4565b90505f8860016118558286613bb4565b61185f9190613d04565b6118699190613cf1565b90506118758282612243565b9a9950505050505050505050565b61190d6040518061012001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f8152602001606081526020015f81526020015f8152602001606081526020015f73ffffffffffffffffffffffffffffffffffffffff1681525090565b604051602481018590526044810184905267ffffffffffffffff831660648201525f90608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f524c0cfa000000000000000000000000000000000000000000000000000000001790528151610120810190925273111111111111111111111111111111111111111182529192509081016119df620100006002613705565b73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f815260200182815260200163044aa200815260200161032081526020015f67ffffffffffffffff811115611a3757611a3761375e565b604051908082528060200260200182016040528015611a6a57816020015b6060815260200190600190039081611a555790505b5081525f60209091015295945050505050565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f90808203611ada576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018114611b14576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4555f6040518060800160405280611b4c611be3565b81526020015f81526020015f42611b639190613bb4565b67ffffffffffffffff1681526020018590529050611b7f612fee565b611b8882612258565b8095508192505050611ba881836060015160e001518685604001516122e9565b505060017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf45550919050565b611bdf60338361236e565b5050565b5f611bee60336123bb565b905090565b6040805160c080820183525f8083526020808401829052838501829052606084018290526080840182905260a08401829052845192830185529082526001828201529192909190820190611c4990850185613d17565b61ffff168152602001611c5f6180006008613705565b73ffffffffffffffffffffffffffffffffffffffff168152602001836020016020810190611c8d9190613d30565b73ffffffffffffffffffffffffffffffffffffffff168152602001611cb56040850185613d4b565b604051611cc3929190613dac565b604051908190039020905292915050565b5f61045287878761047936899003890189613a2d565b5f5f8260600151905060408160e00151511115611d33576040517f76da24b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d3b611be3565b835260c0810151611d4d903a90611657565b6020840181905260a08201515f91611d659190613b82565b9050816060015181611d779190613bb4565b82604001511015611dd1576060820151611d919082613bb4565b60408084015190517fb385a3da000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016103e7565b611de3826101000151835f01516123cd565b73ffffffffffffffffffffffffffffffffffffffff9081166101008401528251163214611e3b5781517311110000000000000000000000000000000011110173ffffffffffffffffffffffffffffffffffffffff1682525b611e455f42613bb4565b67ffffffffffffffff166040850152611e5c612fee565b611e6585612258565b8095508192505050611e8581866060015160e001518688604001516122e9565b60325473ffffffffffffffffffffffffffffffffffffffff1615611f565760325460285460408781015190517fd077255100000000000000000000000000000000000000000000000000000000815260048101929092526024820187905267ffffffffffffffff16604482015273ffffffffffffffffffffffffffffffffffffffff9091169063d0772551906064016020604051808303815f875af1158015611f30573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f549190613bc7565b505b505050919050565b611fa06040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f151581525090565b5f829003611fda576040517f8e23ac1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f8d5411ba4a61cbb507591adfdbe3b8bb500d912bee0b3b4ff0750f652525bc468401612033576040517fd356e6ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61203e8484612464565b805160c0840181905260208201519192505f916120739161206c91889188916120679082613bb4565b61258f565b888861263b565b905081602001518360c00181815161208b9190613bb4565b90525060808301819052606082018051151560e085015251156120af575050610455565b5f6120ba828a6126e2565b90505f86868660c001518181106120d3576120d3613dbb565b905060200201355f1c90508460c00180516120ed90613de8565b905260c085015160408501515f9161211991612112918b918b91906120679082613bb4565b838561263b565b905084604001518660c0018181516121319190613bb4565b90525061213e818d61273d565b8660a0018181525050505050505f5f5f5f87878760c0015181811061216557612165613dbb565b905060200201355f1c90508560c001805161217f90613de8565b905260c0860151608082901c93506fffffffffffffffffffffffffffffffff82169250889088908181106121b5576121b5613dbb565b905060200201355f1c93508560c00180516121cf90613de8565b90525060408051610100810182529384526020840192909252828201528201516060808301919091526080848101519083015260a0808501519083015260c09384015193820193909352910151151560e08201529695505050505050565b606061223b8484848161258f565b949350505050565b5f81831161225157816107da565b5090919050565b612260612fee565b5f61226a8361277e565b91505f8260405160200161227e9190613f8e565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152919052601a546026549192506122d991859184916d0100000000000000000000000000900463ffffffff166128ee565b8080519060200120915050915091565b6122f38282611bd4565b7f4531cd5795773d7101c17bdeb9f5ab7f47d7056017506f937083be5d6e77a3828461010001518383878760405161232f959493929190613fa0565b60405180910390a16101008401516040518391907f779f441679936c5441b671969f37400b8c3ed0071cb47444431bf985754560df905f90a350505050565b5f61237c60038401836129f3565b5f90815260029094016020525050604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905550565b600381015481545f9161040191613bb4565b5f73ffffffffffffffffffffffffffffffffffffffff831661242b5773ffffffffffffffffffffffffffffffffffffffff82163214612422577311110000000000000000000000000000000011118201612424565b815b9050610401565b73ffffffffffffffffffffffffffffffffffffffff83163b15612251577311110000000000000000000000000000000011118301612424565b61248d60405180608001604052805f81526020015f81526020015f81526020015f151581525090565b5f83835f8181106124a0576124a0613dbb565b6020908102929092013592505081901b5f819003612524578160f881901c6001146124fd576040517f79274f0400000000000000000000000000000000000000000000000000000000815260f882901c60048201526024016103e7565b600180855283901a60208501528260021a60408501528260031a151560608501525061253c565b5f808452602084018590526040840152600160608401525b826060015180156125505750604083015115155b15612587576040517f48c5fa2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b606061259b8383613d04565b67ffffffffffffffff8111156125b3576125b361375e565b6040519080825280602002602001820160405280156125dc578160200160208202803683370190505b509050825b82811015612632578585828181106125fb576125fb613dbb565b9050602002013582858361260f9190613d04565b8151811061261f5761261f613dbb565b60209081029190910101526001016125e1565b50949350505050565b82515f906126498482612b7f565b825f5b828110156126d85761265f600287614052565b156126955761269087828151811061267957612679613dbb565b6020026020010151835f9182526020526040902090565b6126c1565b6126c1828883815181106126ab576126ab613dbb565b60200260200101515f9182526020526040902090565b91506126ce600287613cf1565b955060010161264c565b5095945050505050565b604080517fd82fec4a37cbdc47f1e5cc4ad64deacf34a48e6f7c61fa5b68fd58e543259cf46020820152908101839052606081018290525f906080015b60405160208183030381529060405280519060200120905092915050565b604080517f39bc69363bb9e26cf14240de4e22569e95cf175cfbcf1ade1a47a253b4bf7f616020820152908101839052606081018290525f9060800161271f565b612786612fee565b5f8260600151905060405180610200016040528060ff8152602001825f015173ffffffffffffffffffffffffffffffffffffffff168152602001826020015173ffffffffffffffffffffffffffffffffffffffff1681526020018260a0015181526020018260c001518152602001846020015181526020015f81526020015f8152602001845f015181526020018260600151815260200160405180608001604052808460400151815260200184610100015173ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f8152508152602001826080015181526020015f67ffffffffffffffff8111156128855761288561375e565b6040519080825280601f01601f1916602001820160405280156128af576020820181803683370190505b5081526020016128c28360e00151612bf6565b8152604080515f8082526020808301845280850192909252825190815290810182529101529392505050565b5f6128fe85606001518551612c90565b90508281111561293a576040517ff0b4e88f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8185608001518261294b9190613cf1565b111561299d57818560800151826129629190613cf1565b6040517f959f26fb000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016103e7565b806129b48551876101a00151518860800151612ce0565b11156129ec576040517f47b3b14500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b5f5f5f60018560020180549050612a0a9190613d04565b8554909150855f612a1a83613de8565b9091555092506001811b8303612a9c575f856002018281548110612a4057612a40613dbb565b905f5260205f20015490505f612a5f82835f9182526020526040902090565b600288018054600181810183555f9283526020808420909201849055808b0180549182018155835290822001559050612a9783613de8565b925050505b82845f805b848163ffffffff161015612b60575f612abb600286614052565b159050808015612ac9575082155b15612aea5760018a015f90815260209020849063ffffffff84160155600192505b612b4581612b0c5760018b015f9081526020902063ffffffff84160154612b0e565b845b82612b2257855f9182526020526040902090565b60028c015f9081526020902063ffffffff851601545f9182526020526040902090565b9350600185901c94505080612b5990614065565b9050612aa1565b50600188015f90815260209020829085015550925050505b9250929050565b6101008110612bba576040517f1c50038500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001811b8210611bdf576040517f9bb54c3500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516060908067ffffffffffffffff811115612c1457612c1461375e565b604051908082528060200260200182016040528015612c3d578160200160208202803683370190505b5091505f5b81811015612c89575f612c6d858381518110612c6057612c60613dbb565b6020026020010151612d69565b6001830160200285015250612c828160010190565b9050612c42565b5050919050565b5f5f612c9b83612ee8565b905080841015612cd7576040517f2e311df800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b90920392915050565b5f62028cf5612cfc612cf461067887613b82565b610220612f03565b612d069082613bb4565b9050612d146109a985613b82565b612d1e9082613bb4565b9050612d2d816202a5ac612243565b90505f612d3b846058613b82565b905083612d49604087613b82565b612d539190613b82565b612d5d9082613bb4565b90506104558183613bb4565b5f60208251612d789190614052565b15612db45781516040517fe37d2c020000000000000000000000000000000000000000000000000000000081526004016103e791815260200190565b5f60208351612dc39190613cf1565b9050620100008110612e045760016040517f43e266b00000000000000000000000000000000000000000000000000000000081526004016103e79190614089565b612e0f600282614052565b5f03612e4a5760036040517f43e266b00000000000000000000000000000000000000000000000000000000081526004016103e79190614089565b600283604051612e5a91906140c8565b602060405180830381855afa158015612e75573d5f5f3e3d5ffd5b5050506040513d601f19601f82011682018060405250810190612e989190613bc7565b60e09190911b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff91909116177f01000000000000000000000000000000000000000000000000000000000000001792915050565b6127105f612ef783600a613b82565b90506107da8282612243565b5f8215612f305781612f16600185613d04565b612f209190613cf1565b612f2b906001613bb4565b6107da565b505f92915050565b60405180608001604052805f81526020015f81526020015f67ffffffffffffffff168152602001612fe96040518061012001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f8152602001606081526020015f81526020015f8152602001606081526020015f73ffffffffffffffffffffffffffffffffffffffff1681525090565b905290565b6040518061020001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f815260200161303e613067565b815260200160608152602001606081526020016060815260200160608152602001606081525090565b60405180608001604052806004906020820280368337509192915050565b803561ffff81168114613096575f5ffd5b919050565b5f5f83601f8401126130ab575f5ffd5b50813567ffffffffffffffff8111156130c2575f5ffd5b6020830191508360208260051b8501011115612b78575f5ffd5b5f5f5f5f5f5f5f60c0888a0312156130f2575f5ffd5b87359650602088013595506040880135945061311060608901613085565b9350608088013567ffffffffffffffff81111561312b575f5ffd5b6131378a828b0161309b565b90945092505060a08801356002811061314e575f5ffd5b8091505092959891949750929550565b5f6020828403121561316e575f5ffd5b813567ffffffffffffffff811115613184575f5ffd5b820161012081850312156107da575f5ffd5b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6107da6020830184613196565b5f60608284031215613204575f5ffd5b50919050565b5f5f5f5f5f5f60a0878903121561321f575f5ffd5b863595506020870135945060408701359350606087013567ffffffffffffffff81111561324a575f5ffd5b61325689828a016131f4565b935050608087013567ffffffffffffffff811115613272575f5ffd5b61327e89828a0161309b565b979a9699509497509295939492505050565b5f60c08284031215613204575f5ffd5b5f5f5f5f5f61012086880312156132b5575f5ffd5b85359450602086013593506132cd8760408801613290565b925061010086013567ffffffffffffffff8111156132e9575f5ffd5b6132f58882890161309b565b969995985093965092949392505050565b5f5f83601f840112613316575f5ffd5b50813567ffffffffffffffff81111561332d575f5ffd5b602083019150836020828501011115612b78575f5ffd5b5f5f5f5f5f5f5f60a0888a03121561335a575f5ffd5b873596506020880135955061337160408901613085565b9450606088013567ffffffffffffffff81111561338c575f5ffd5b6133988a828b01613306565b909550935050608088013567ffffffffffffffff8111156133b7575f5ffd5b6133c38a828b0161309b565b989b979a50959850939692959293505050565b5f5f5f5f5f5f60a087890312156133eb575f5ffd5b86359550602087013594506040870135935060608701359250608087013567ffffffffffffffff811115613272575f5ffd5b5f5f5f5f5f60808688031215613431575f5ffd5b853594506020860135935060408601359250606086013567ffffffffffffffff8111156132e9575f5ffd5b5f5f5f6060848603121561346e575f5ffd5b505081359360208301359350604090920135919050565b803567ffffffffffffffff81168114613096575f5ffd5b5f5f5f606084860312156134ae575f5ffd5b83359250602084013591506134c560408501613485565b90509250925092565b73ffffffffffffffffffffffffffffffffffffffff811681146134ef575f5ffd5b50565b8035613096816134ce565b5f5f5f6040848603121561350f575f5ffd5b833561351a816134ce565b9250602084013567ffffffffffffffff811115613535575f5ffd5b61354186828701613306565b9497909650939450505050565b5f5f6040838503121561355f575f5ffd5b8235915061356f60208401613485565b90509250929050565b5f5f5f5f5f6080868803121561358c575f5ffd5b8535945060208601359350604086013567ffffffffffffffff8111156135b0575f5ffd5b6135bc888289016131f4565b935050606086013567ffffffffffffffff8111156132e9575f5ffd5b5f5f5f5f5f5f61014087890312156135ee575f5ffd5b86359550602087013594506040870135935061360d8860608901613290565b925061012087013567ffffffffffffffff811115613272575f5ffd5b5f5f5f5f5f5f5f5f5f60e08a8c031215613641575f5ffd5b893561364c816134ce565b985060208a0135975060408a013567ffffffffffffffff81111561366e575f5ffd5b61367a8c828d01613306565b90985096505060608a0135945060808a0135935060a08a013567ffffffffffffffff8111156136a7575f5ffd5b6136b38c828d0161309b565b90945092505060c08a01356136c7816134ce565b809150509295985092959850929598565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b73ffffffffffffffffffffffffffffffffffffffff8181168382160190811115610401576104016136d8565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051610120810167ffffffffffffffff811182821017156137af576137af61375e565b60405290565b60405160c0810167ffffffffffffffff811182821017156137af576137af61375e565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561381f5761381f61375e565b604052919050565b5f82601f830112613836575f5ffd5b813567ffffffffffffffff8111156138505761385061375e565b61388160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016137d8565b818152846020838601011115613895575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f67ffffffffffffffff8311156138ca576138ca61375e565b8260051b6138da602082016137d8565b8481529150820160208201858211156138f1575f5ffd5b835b8281101561392c57803567ffffffffffffffff811115613911575f5ffd5b61391d88828801613827565b835250602091820191016138f3565b5050509392505050565b5f82601f830112613945575f5ffd5b6107da838335602085016138b1565b5f6101208236031215613965575f5ffd5b61396d61378b565b613976836134f2565b8152613984602084016134f2565b60208201526040838101359082015260608084013590820152608083013567ffffffffffffffff8111156139b6575f5ffd5b6139c236828601613827565b60808301525060a0838101359082015260c0808401359082015260e083013567ffffffffffffffff8111156139f5575f5ffd5b613a0136828601613936565b60e083015250613a1461010084016134f2565b61010082015292915050565b80151581146134ef575f5ffd5b5f60c0828403128015613a3e575f5ffd5b50613a476137b5565b823560ff81168114613a57575f5ffd5b81526020830135613a6781613a20565b6020820152613a7860408401613085565b60408201526060830135613a8b816134ce565b60608201526080838101359082015260a0928301359281019290925250919050565b5f60208284031215613abd575f5ffd5b81516107da816134ce565b88815287602082015286604082015261ffff8616606082015260c060808201528360c0820152838560e08301375f60e085830101525f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820160e08382030160a08401528360e08201527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613b63575f5ffd5b8360051b808661010084013701610100019a9950505050505050505050565b8082028115828204841417610401576104016136d8565b5f60208284031215613ba9575f5ffd5b81516107da81613a20565b80820180821115610401576104016136d8565b5f60208284031215613bd7575f5ffd5b5051919050565b5f6107da3684846138b1565b73ffffffffffffffffffffffffffffffffffffffff8716815273ffffffffffffffffffffffffffffffffffffffff8616602082015284604082015283606082015260c060808201525f613c4060c0830185613196565b905073ffffffffffffffffffffffffffffffffffffffff831660a0830152979650505050505050565b5f608082018683528560208401528460408401526080606084015280845180835260a0850191506020860192505f5b81811015613cb6578351835260209384019390920191600101613c98565b509098975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f82613cff57613cff613cc4565b500490565b81810381811115610401576104016136d8565b5f60208284031215613d27575f5ffd5b6107da82613085565b5f60208284031215613d40575f5ffd5b81356107da816134ce565b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613d7e575f5ffd5b83018035915067ffffffffffffffff821115613d98575f5ffd5b602001915036819003821315612b78575f5ffd5b818382375f9101908152919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613e1857613e186136d8565b5060010190565b805f5b6004811015613e41578151845260209384019390910190600101613e22565b50505050565b5f8151808452602084019350602083015f5b82811015613e77578151865260209586019590910190600101613e59565b5093949350505050565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301526101008101516101008301526101208101516101208301525f610140820151613ef8610140850182613e1f565b506101608201516102606101c0850152613f16610260850182613196565b90506101808301518482036101e0860152613f318282613196565b9150506101a0830151848203610200860152613f4d8282613e47565b9150506101c0830151848203610220860152613f698282613196565b9150506101e0830151848203610240860152613f858282613196565b95945050505050565b602081525f6107da6020830184613e81565b85815284602082015267ffffffffffffffff8416604082015260a060608201525f613fce60a0830185613e81565b828103608084015280845180835260208301915060208160051b840101602087015f5b83811015614041577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301855261402b838351613196565b6020958601959093509190910190600101613ff1565b50909b9a5050505050505050505050565b5f8261406057614060613cc4565b500690565b5f63ffffffff821663ffffffff8103614080576140806136d8565b60010192915050565b60208101600483106140c2577f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b91905290565b5f82518060208501845e5f92019182525091905056fea2646970667358221220cef29b34a13bc6a389d40170e8413cbe83888570b14fe3e2e4b970871bd681c964736f6c634300081c0033000000000000000000000000000000000000000000000000000000000000010e0000000000000000000000000000000000000000000000000000000000aa36a7
Deployed Bytecode
0x6080604052600436106100e4575f3560e01c80637efda2ae11610087578063ddcc9eec11610057578063ddcc9eec14610298578063e4948f43146102b7578063e896760d146102d6578063eb672419146102f5575f5ffd5b80637efda2ae1461021c578063b473318e1461023b578063d07725511461025a578063d07b90d114610279575f5ffd5b806318b7fc22116100c257806318b7fc221461019e578063263b7f8e146101bd5780636c0960f9146101dc57806379cf6165146101fd575f5ffd5b8063042901c7146100e857806312f43dab1461011c57806317d7de7c14610149575b5f5ffd5b3480156100f3575f5ffd5b506101076101023660046130dc565b610308565b60405190151581526020015b60405180910390f35b348015610127575f5ffd5b5061013b61013636600461315e565b610396565b604051908152602001610113565b348015610154575f5ffd5b506101916040518060400160405280600c81526020017f4d61696c626f784661636574000000000000000000000000000000000000000081525081565b60405161011391906131e2565b3480156101a9575f5ffd5b506101076101b836600461320a565b610407565b3480156101c8575f5ffd5b506101076101d73660046132a0565b61045f565b3480156101e7575f5ffd5b506101fb6101f6366004613344565b610480565b005b348015610208575f5ffd5b506101076102173660046133d6565b610760565b348015610227575f5ffd5b5061010761023636600461341d565b6107ab565b348015610246575f5ffd5b5061013b61025536600461345c565b6107be565b348015610265575f5ffd5b5061013b61027436600461349c565b6107e1565b348015610284575f5ffd5b5061013b6102933660046134fd565b610a02565b3480156102a3575f5ffd5b506101fb6102b236600461354e565b610c09565b3480156102c2575f5ffd5b506101076102d1366004613578565b610cec565b3480156102e1575f5ffd5b506101076102f03660046135d8565b610d00565b61013b610303366004613629565b610d4b565b5f5f6040518060c001604052805f60ff1681526020016001151581526020018761ffff16815260200161800060016103409190613705565b73ffffffffffffffffffffffffffffffffffffffff1681526020018a815260200184600181111561037357610373613731565b9052602854909150610389908989848989611006565b9998505050505050505050565b6029545f9073ffffffffffffffffffffffffffffffffffffffff1633146103f0576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b6104016103fc83613954565b611105565b92915050565b6028545f908714610444576040517f7a47c9a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61045287878787878761134c565b90505b9695505050505050565b6028545f9061045590878761047936899003890189613a2d565b8787611006565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f8190036104dc576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018114610516576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4557f0000000000000000000000000000000000000000000000000000000000aa36a74614610595576040517f87470e360000000000000000000000000000000000000000000000000000000081524660048201526024016103e7565b6028547f000000000000000000000000000000000000000000000000000000000000010e146105f0576040517ff3ed9dfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602954604080517fbc0aac1000000000000000000000000000000000000000000000000000000000815290515f9273ffffffffffffffffffffffffffffffffffffffff169163bc0aac109160048083019260209291908290030181865afa15801561065d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106819190613aad565b6040517fc87325f100000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063c87325f190610704907f000000000000000000000000000000000000000000000000000000000000010e908d908d908d908d908d908d908d90600401613ac8565b5f604051808303815f87803b15801561071b575f5ffd5b505af115801561072d573d5f5f3e3d5ffd5b505050505060017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4555050505050505050565b6028545f90871461079d576040517f7a47c9a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61045287878787878761135c565b5f6104555f602801548787878787611367565b5f5f6107ca8584611657565b90506107d68482613b82565b9150505b9392505050565b5f7f0000000000000000000000000000000000000000000000000000000000aa36a7461461083d576040517f87470e360000000000000000000000000000000000000000000000000000000081524660048201526024016103e7565b6029546028546040517fe9420f8c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092169163e9420f8c916108989160040190815260200190565b602060405180830381865afa1580156108b3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108d79190613b99565b61090d576040517fd0266e2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6029546040517fe680c4c100000000000000000000000000000000000000000000000000000000815260048101869052339173ffffffffffffffffffffffffffffffffffffffff169063e680c4c190602401602060405180830381865afa15801561097a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061099e9190613aad565b73ffffffffffffffffffffffffffffffffffffffff16146109eb576040517f32ddf9a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6109f7858585611883565b90506107d681611a7d565b5f333014610a3e576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016103e7565b610b2260405180610120016040528073ffffffffffffffffffffffffffffffffffffffff801681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f815260200185858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250938552505063044aa2006020840152506103206040830152606090910190604051908082528060200260200182016040528015610b1357816020015b6060815260200190600190039081610afe5790505b5081525f602090910152611a7d565b60325490915073ffffffffffffffffffffffffffffffffffffffff16156107da5760325460285473ffffffffffffffffffffffffffffffffffffffff9091169063d07725519083610b735f42613bb4565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526004810193909352602483019190915267ffffffffffffffff1660448201526064016020604051808303815f875af1158015610bdd573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c019190613bc7565b509392505050565b60295473ffffffffffffffffffffffffffffffffffffffff163314610c5c576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016103e7565b610c668282611bd4565b7f0137d2eaa6ec5b7e4f233f6d6f441410014535d0f3985367994c94bf15a2a564610c8f611be3565b604080519182526020820185905267ffffffffffffffff84169082015260600160405180910390a181610cc0611be3565b6040517f779f441679936c5441b671969f37400b8c3ed0071cb47444431bf985754560df905f90a35050565b5f6104555f60280154878761047988611bf3565b6028545f908714610d3d576040517f7a47c9a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610452878787878787611cd4565b5f7f0000000000000000000000000000000000000000000000000000000000aa36a74614610da7576040517f87470e360000000000000000000000000000000000000000000000000000000081524660048201526024016103e7565b6028547f000000000000000000000000000000000000000000000000000000000000010e14610e02576040517ff3ed9dfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ecc6040518061012001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018c73ffffffffffffffffffffffffffffffffffffffff1681526020013481526020018b81526020018a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505050908252506020810189905260408101889052606001610ea88688613bde565b81526020018473ffffffffffffffffffffffffffffffffffffffff16815250611105565b90505f5f6029015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bc0aac106040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f3b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f5f9190613aad565b6028546031546040517fc487944000000000000000000000000000000000000000000000000000000000815260048101929092526024820152336044820152346064820181905291925073ffffffffffffffffffffffffffffffffffffffff83169163c4879440916084015f604051808303818588803b158015610fe1575f5ffd5b505af1158015610ff3573d5f5f3e3d5ffd5b5050505050509998505050505050505050565b5f5f845f015185602001518660400151876060015188608001518960a001516040516020016110d39695949392919060f896871b7fff0000000000000000000000000000000000000000000000000000000000000016815294151590951b600185015260f09290921b7fffff00000000000000000000000000000000000000000000000000000000000016600284015260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660048301526018820152603881019190915260580190565b6040516020818303038152906040528051906020012090506110f9888888848888611367565b98975050505050505050565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f90808203611162576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001811461119c576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf455602e5473ffffffffffffffffffffffffffffffffffffffff16156112c957602e5483516020850151604080870151606088015160808901516101008a015193517fcbcf2e3c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9097169663cbcf2e3c966112559690959094939291600401613bea565b602060405180830381865afa158015611270573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112949190613b99565b6112c9576040517ec5a6a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103208360c0015114611308576040517fc91cf3b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611310612f38565b6060810184905261132081611cea565b60017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf455949350505050565b5f61045287878761047988611bf3565b5f6104528787878787875b5f5f611377888888888888611f5e565b90508060e001511561141557600b548711156113c2576040517f2078a6a0000000000000000000000000000000000000000000000000000000008152600481018890526024016103e7565b5f878152600f602052604090205480611407576040517fa969e48600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b608090910151149050610455565b5f878152600f60205260409020541561145a576040517fbdaf7d4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60295481516040517fe9420f8c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092169163e9420f8c916114b49160040190815260200190565b602060405180830381865afa1580156114cf573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114f39190613b99565b611529576040517fd0266e2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60295481516040517fe680c4c100000000000000000000000000000000000000000000000000000000815260048101919091525f9173ffffffffffffffffffffffffffffffffffffffff169063e680c4c190602401602060405180830381865afa158015611599573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115bd9190613aad565b90508073ffffffffffffffffffffffffffffffffffffffff16637efda2ae836020015184604001518560a001516115f98a8a8960c0015161222d565b6040518563ffffffff1660e01b81526004016116189493929190613c69565b602060405180830381865afa158015611633573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103899190613b99565b6040805160c08101909152602680545f9283929091829060ff16600181111561168257611682613731565b600181111561169357611693613731565b81529054610100810463ffffffff90811660208401526501000000000082048116604084015269010000000000000000008204811660608401526d0100000000000000000000000000820416608083015271010000000000000000000000000000000000900467ffffffffffffffff1660a090910152602d5490915070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff165f0361176d576040517f6ef9a97200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602d545f906fffffffffffffffffffffffffffffffff70010000000000000000000000000000000082048116916117a5911687613b82565b6117af9190613cf1565b90505f80835160018111156117c6576117c6613731565b036117d9576117d6826011613b82565b90505b5f82846020015163ffffffff166117f09190613b82565b90505f846040015163ffffffff16826118099190613cf1565b6118139084613bb4565b90505f856060015163ffffffff168361182c9190613cf1565b8660a0015167ffffffffffffffff166118459190613bb4565b90505f8860016118558286613bb4565b61185f9190613d04565b6118699190613cf1565b90506118758282612243565b9a9950505050505050505050565b61190d6040518061012001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f8152602001606081526020015f81526020015f8152602001606081526020015f73ffffffffffffffffffffffffffffffffffffffff1681525090565b604051602481018590526044810184905267ffffffffffffffff831660648201525f90608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f524c0cfa000000000000000000000000000000000000000000000000000000001790528151610120810190925273111111111111111111111111111111111111111182529192509081016119df620100006002613705565b73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f815260200182815260200163044aa200815260200161032081526020015f67ffffffffffffffff811115611a3757611a3761375e565b604051908082528060200260200182016040528015611a6a57816020015b6060815260200190600190039081611a555790505b5081525f60209091015295945050505050565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f90808203611ada576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018114611b14576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4555f6040518060800160405280611b4c611be3565b81526020015f81526020015f42611b639190613bb4565b67ffffffffffffffff1681526020018590529050611b7f612fee565b611b8882612258565b8095508192505050611ba881836060015160e001518685604001516122e9565b505060017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf45550919050565b611bdf60338361236e565b5050565b5f611bee60336123bb565b905090565b6040805160c080820183525f8083526020808401829052838501829052606084018290526080840182905260a08401829052845192830185529082526001828201529192909190820190611c4990850185613d17565b61ffff168152602001611c5f6180006008613705565b73ffffffffffffffffffffffffffffffffffffffff168152602001836020016020810190611c8d9190613d30565b73ffffffffffffffffffffffffffffffffffffffff168152602001611cb56040850185613d4b565b604051611cc3929190613dac565b604051908190039020905292915050565b5f61045287878761047936899003890189613a2d565b5f5f8260600151905060408160e00151511115611d33576040517f76da24b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d3b611be3565b835260c0810151611d4d903a90611657565b6020840181905260a08201515f91611d659190613b82565b9050816060015181611d779190613bb4565b82604001511015611dd1576060820151611d919082613bb4565b60408084015190517fb385a3da000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016103e7565b611de3826101000151835f01516123cd565b73ffffffffffffffffffffffffffffffffffffffff9081166101008401528251163214611e3b5781517311110000000000000000000000000000000011110173ffffffffffffffffffffffffffffffffffffffff1682525b611e455f42613bb4565b67ffffffffffffffff166040850152611e5c612fee565b611e6585612258565b8095508192505050611e8581866060015160e001518688604001516122e9565b60325473ffffffffffffffffffffffffffffffffffffffff1615611f565760325460285460408781015190517fd077255100000000000000000000000000000000000000000000000000000000815260048101929092526024820187905267ffffffffffffffff16604482015273ffffffffffffffffffffffffffffffffffffffff9091169063d0772551906064016020604051808303815f875af1158015611f30573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f549190613bc7565b505b505050919050565b611fa06040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f151581525090565b5f829003611fda576040517f8e23ac1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f8d5411ba4a61cbb507591adfdbe3b8bb500d912bee0b3b4ff0750f652525bc468401612033576040517fd356e6ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61203e8484612464565b805160c0840181905260208201519192505f916120739161206c91889188916120679082613bb4565b61258f565b888861263b565b905081602001518360c00181815161208b9190613bb4565b90525060808301819052606082018051151560e085015251156120af575050610455565b5f6120ba828a6126e2565b90505f86868660c001518181106120d3576120d3613dbb565b905060200201355f1c90508460c00180516120ed90613de8565b905260c085015160408501515f9161211991612112918b918b91906120679082613bb4565b838561263b565b905084604001518660c0018181516121319190613bb4565b90525061213e818d61273d565b8660a0018181525050505050505f5f5f5f87878760c0015181811061216557612165613dbb565b905060200201355f1c90508560c001805161217f90613de8565b905260c0860151608082901c93506fffffffffffffffffffffffffffffffff82169250889088908181106121b5576121b5613dbb565b905060200201355f1c93508560c00180516121cf90613de8565b90525060408051610100810182529384526020840192909252828201528201516060808301919091526080848101519083015260a0808501519083015260c09384015193820193909352910151151560e08201529695505050505050565b606061223b8484848161258f565b949350505050565b5f81831161225157816107da565b5090919050565b612260612fee565b5f61226a8361277e565b91505f8260405160200161227e9190613f8e565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152919052601a546026549192506122d991859184916d0100000000000000000000000000900463ffffffff166128ee565b8080519060200120915050915091565b6122f38282611bd4565b7f4531cd5795773d7101c17bdeb9f5ab7f47d7056017506f937083be5d6e77a3828461010001518383878760405161232f959493929190613fa0565b60405180910390a16101008401516040518391907f779f441679936c5441b671969f37400b8c3ed0071cb47444431bf985754560df905f90a350505050565b5f61237c60038401836129f3565b5f90815260029094016020525050604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905550565b600381015481545f9161040191613bb4565b5f73ffffffffffffffffffffffffffffffffffffffff831661242b5773ffffffffffffffffffffffffffffffffffffffff82163214612422577311110000000000000000000000000000000011118201612424565b815b9050610401565b73ffffffffffffffffffffffffffffffffffffffff83163b15612251577311110000000000000000000000000000000011118301612424565b61248d60405180608001604052805f81526020015f81526020015f81526020015f151581525090565b5f83835f8181106124a0576124a0613dbb565b6020908102929092013592505081901b5f819003612524578160f881901c6001146124fd576040517f79274f0400000000000000000000000000000000000000000000000000000000815260f882901c60048201526024016103e7565b600180855283901a60208501528260021a60408501528260031a151560608501525061253c565b5f808452602084018590526040840152600160608401525b826060015180156125505750604083015115155b15612587576040517f48c5fa2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b606061259b8383613d04565b67ffffffffffffffff8111156125b3576125b361375e565b6040519080825280602002602001820160405280156125dc578160200160208202803683370190505b509050825b82811015612632578585828181106125fb576125fb613dbb565b9050602002013582858361260f9190613d04565b8151811061261f5761261f613dbb565b60209081029190910101526001016125e1565b50949350505050565b82515f906126498482612b7f565b825f5b828110156126d85761265f600287614052565b156126955761269087828151811061267957612679613dbb565b6020026020010151835f9182526020526040902090565b6126c1565b6126c1828883815181106126ab576126ab613dbb565b60200260200101515f9182526020526040902090565b91506126ce600287613cf1565b955060010161264c565b5095945050505050565b604080517fd82fec4a37cbdc47f1e5cc4ad64deacf34a48e6f7c61fa5b68fd58e543259cf46020820152908101839052606081018290525f906080015b60405160208183030381529060405280519060200120905092915050565b604080517f39bc69363bb9e26cf14240de4e22569e95cf175cfbcf1ade1a47a253b4bf7f616020820152908101839052606081018290525f9060800161271f565b612786612fee565b5f8260600151905060405180610200016040528060ff8152602001825f015173ffffffffffffffffffffffffffffffffffffffff168152602001826020015173ffffffffffffffffffffffffffffffffffffffff1681526020018260a0015181526020018260c001518152602001846020015181526020015f81526020015f8152602001845f015181526020018260600151815260200160405180608001604052808460400151815260200184610100015173ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f8152508152602001826080015181526020015f67ffffffffffffffff8111156128855761288561375e565b6040519080825280601f01601f1916602001820160405280156128af576020820181803683370190505b5081526020016128c28360e00151612bf6565b8152604080515f8082526020808301845280850192909252825190815290810182529101529392505050565b5f6128fe85606001518551612c90565b90508281111561293a576040517ff0b4e88f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8185608001518261294b9190613cf1565b111561299d57818560800151826129629190613cf1565b6040517f959f26fb000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016103e7565b806129b48551876101a00151518860800151612ce0565b11156129ec576040517f47b3b14500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b5f5f5f60018560020180549050612a0a9190613d04565b8554909150855f612a1a83613de8565b9091555092506001811b8303612a9c575f856002018281548110612a4057612a40613dbb565b905f5260205f20015490505f612a5f82835f9182526020526040902090565b600288018054600181810183555f9283526020808420909201849055808b0180549182018155835290822001559050612a9783613de8565b925050505b82845f805b848163ffffffff161015612b60575f612abb600286614052565b159050808015612ac9575082155b15612aea5760018a015f90815260209020849063ffffffff84160155600192505b612b4581612b0c5760018b015f9081526020902063ffffffff84160154612b0e565b845b82612b2257855f9182526020526040902090565b60028c015f9081526020902063ffffffff851601545f9182526020526040902090565b9350600185901c94505080612b5990614065565b9050612aa1565b50600188015f90815260209020829085015550925050505b9250929050565b6101008110612bba576040517f1c50038500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001811b8210611bdf576040517f9bb54c3500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516060908067ffffffffffffffff811115612c1457612c1461375e565b604051908082528060200260200182016040528015612c3d578160200160208202803683370190505b5091505f5b81811015612c89575f612c6d858381518110612c6057612c60613dbb565b6020026020010151612d69565b6001830160200285015250612c828160010190565b9050612c42565b5050919050565b5f5f612c9b83612ee8565b905080841015612cd7576040517f2e311df800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b90920392915050565b5f62028cf5612cfc612cf461067887613b82565b610220612f03565b612d069082613bb4565b9050612d146109a985613b82565b612d1e9082613bb4565b9050612d2d816202a5ac612243565b90505f612d3b846058613b82565b905083612d49604087613b82565b612d539190613b82565b612d5d9082613bb4565b90506104558183613bb4565b5f60208251612d789190614052565b15612db45781516040517fe37d2c020000000000000000000000000000000000000000000000000000000081526004016103e791815260200190565b5f60208351612dc39190613cf1565b9050620100008110612e045760016040517f43e266b00000000000000000000000000000000000000000000000000000000081526004016103e79190614089565b612e0f600282614052565b5f03612e4a5760036040517f43e266b00000000000000000000000000000000000000000000000000000000081526004016103e79190614089565b600283604051612e5a91906140c8565b602060405180830381855afa158015612e75573d5f5f3e3d5ffd5b5050506040513d601f19601f82011682018060405250810190612e989190613bc7565b60e09190911b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff91909116177f01000000000000000000000000000000000000000000000000000000000000001792915050565b6127105f612ef783600a613b82565b90506107da8282612243565b5f8215612f305781612f16600185613d04565b612f209190613cf1565b612f2b906001613bb4565b6107da565b505f92915050565b60405180608001604052805f81526020015f81526020015f67ffffffffffffffff168152602001612fe96040518061012001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f8152602001606081526020015f81526020015f8152602001606081526020015f73ffffffffffffffffffffffffffffffffffffffff1681525090565b905290565b6040518061020001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f815260200161303e613067565b815260200160608152602001606081526020016060815260200160608152602001606081525090565b60405180608001604052806004906020820280368337509192915050565b803561ffff81168114613096575f5ffd5b919050565b5f5f83601f8401126130ab575f5ffd5b50813567ffffffffffffffff8111156130c2575f5ffd5b6020830191508360208260051b8501011115612b78575f5ffd5b5f5f5f5f5f5f5f60c0888a0312156130f2575f5ffd5b87359650602088013595506040880135945061311060608901613085565b9350608088013567ffffffffffffffff81111561312b575f5ffd5b6131378a828b0161309b565b90945092505060a08801356002811061314e575f5ffd5b8091505092959891949750929550565b5f6020828403121561316e575f5ffd5b813567ffffffffffffffff811115613184575f5ffd5b820161012081850312156107da575f5ffd5b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6107da6020830184613196565b5f60608284031215613204575f5ffd5b50919050565b5f5f5f5f5f5f60a0878903121561321f575f5ffd5b863595506020870135945060408701359350606087013567ffffffffffffffff81111561324a575f5ffd5b61325689828a016131f4565b935050608087013567ffffffffffffffff811115613272575f5ffd5b61327e89828a0161309b565b979a9699509497509295939492505050565b5f60c08284031215613204575f5ffd5b5f5f5f5f5f61012086880312156132b5575f5ffd5b85359450602086013593506132cd8760408801613290565b925061010086013567ffffffffffffffff8111156132e9575f5ffd5b6132f58882890161309b565b969995985093965092949392505050565b5f5f83601f840112613316575f5ffd5b50813567ffffffffffffffff81111561332d575f5ffd5b602083019150836020828501011115612b78575f5ffd5b5f5f5f5f5f5f5f60a0888a03121561335a575f5ffd5b873596506020880135955061337160408901613085565b9450606088013567ffffffffffffffff81111561338c575f5ffd5b6133988a828b01613306565b909550935050608088013567ffffffffffffffff8111156133b7575f5ffd5b6133c38a828b0161309b565b989b979a50959850939692959293505050565b5f5f5f5f5f5f60a087890312156133eb575f5ffd5b86359550602087013594506040870135935060608701359250608087013567ffffffffffffffff811115613272575f5ffd5b5f5f5f5f5f60808688031215613431575f5ffd5b853594506020860135935060408601359250606086013567ffffffffffffffff8111156132e9575f5ffd5b5f5f5f6060848603121561346e575f5ffd5b505081359360208301359350604090920135919050565b803567ffffffffffffffff81168114613096575f5ffd5b5f5f5f606084860312156134ae575f5ffd5b83359250602084013591506134c560408501613485565b90509250925092565b73ffffffffffffffffffffffffffffffffffffffff811681146134ef575f5ffd5b50565b8035613096816134ce565b5f5f5f6040848603121561350f575f5ffd5b833561351a816134ce565b9250602084013567ffffffffffffffff811115613535575f5ffd5b61354186828701613306565b9497909650939450505050565b5f5f6040838503121561355f575f5ffd5b8235915061356f60208401613485565b90509250929050565b5f5f5f5f5f6080868803121561358c575f5ffd5b8535945060208601359350604086013567ffffffffffffffff8111156135b0575f5ffd5b6135bc888289016131f4565b935050606086013567ffffffffffffffff8111156132e9575f5ffd5b5f5f5f5f5f5f61014087890312156135ee575f5ffd5b86359550602087013594506040870135935061360d8860608901613290565b925061012087013567ffffffffffffffff811115613272575f5ffd5b5f5f5f5f5f5f5f5f5f60e08a8c031215613641575f5ffd5b893561364c816134ce565b985060208a0135975060408a013567ffffffffffffffff81111561366e575f5ffd5b61367a8c828d01613306565b90985096505060608a0135945060808a0135935060a08a013567ffffffffffffffff8111156136a7575f5ffd5b6136b38c828d0161309b565b90945092505060c08a01356136c7816134ce565b809150509295985092959850929598565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b73ffffffffffffffffffffffffffffffffffffffff8181168382160190811115610401576104016136d8565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051610120810167ffffffffffffffff811182821017156137af576137af61375e565b60405290565b60405160c0810167ffffffffffffffff811182821017156137af576137af61375e565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561381f5761381f61375e565b604052919050565b5f82601f830112613836575f5ffd5b813567ffffffffffffffff8111156138505761385061375e565b61388160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016137d8565b818152846020838601011115613895575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f67ffffffffffffffff8311156138ca576138ca61375e565b8260051b6138da602082016137d8565b8481529150820160208201858211156138f1575f5ffd5b835b8281101561392c57803567ffffffffffffffff811115613911575f5ffd5b61391d88828801613827565b835250602091820191016138f3565b5050509392505050565b5f82601f830112613945575f5ffd5b6107da838335602085016138b1565b5f6101208236031215613965575f5ffd5b61396d61378b565b613976836134f2565b8152613984602084016134f2565b60208201526040838101359082015260608084013590820152608083013567ffffffffffffffff8111156139b6575f5ffd5b6139c236828601613827565b60808301525060a0838101359082015260c0808401359082015260e083013567ffffffffffffffff8111156139f5575f5ffd5b613a0136828601613936565b60e083015250613a1461010084016134f2565b61010082015292915050565b80151581146134ef575f5ffd5b5f60c0828403128015613a3e575f5ffd5b50613a476137b5565b823560ff81168114613a57575f5ffd5b81526020830135613a6781613a20565b6020820152613a7860408401613085565b60408201526060830135613a8b816134ce565b60608201526080838101359082015260a0928301359281019290925250919050565b5f60208284031215613abd575f5ffd5b81516107da816134ce565b88815287602082015286604082015261ffff8616606082015260c060808201528360c0820152838560e08301375f60e085830101525f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820160e08382030160a08401528360e08201527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613b63575f5ffd5b8360051b808661010084013701610100019a9950505050505050505050565b8082028115828204841417610401576104016136d8565b5f60208284031215613ba9575f5ffd5b81516107da81613a20565b80820180821115610401576104016136d8565b5f60208284031215613bd7575f5ffd5b5051919050565b5f6107da3684846138b1565b73ffffffffffffffffffffffffffffffffffffffff8716815273ffffffffffffffffffffffffffffffffffffffff8616602082015284604082015283606082015260c060808201525f613c4060c0830185613196565b905073ffffffffffffffffffffffffffffffffffffffff831660a0830152979650505050505050565b5f608082018683528560208401528460408401526080606084015280845180835260a0850191506020860192505f5b81811015613cb6578351835260209384019390920191600101613c98565b509098975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f82613cff57613cff613cc4565b500490565b81810381811115610401576104016136d8565b5f60208284031215613d27575f5ffd5b6107da82613085565b5f60208284031215613d40575f5ffd5b81356107da816134ce565b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613d7e575f5ffd5b83018035915067ffffffffffffffff821115613d98575f5ffd5b602001915036819003821315612b78575f5ffd5b818382375f9101908152919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613e1857613e186136d8565b5060010190565b805f5b6004811015613e41578151845260209384019390910190600101613e22565b50505050565b5f8151808452602084019350602083015f5b82811015613e77578151865260209586019590910190600101613e59565b5093949350505050565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301526101008101516101008301526101208101516101208301525f610140820151613ef8610140850182613e1f565b506101608201516102606101c0850152613f16610260850182613196565b90506101808301518482036101e0860152613f318282613196565b9150506101a0830151848203610200860152613f4d8282613e47565b9150506101c0830151848203610220860152613f698282613196565b9150506101e0830151848203610240860152613f858282613196565b95945050505050565b602081525f6107da6020830184613e81565b85815284602082015267ffffffffffffffff8416604082015260a060608201525f613fce60a0830185613e81565b828103608084015280845180835260208301915060208160051b840101602087015f5b83811015614041577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301855261402b838351613196565b6020958601959093509190910190600101613ff1565b50909b9a5050505050505050505050565b5f8261406057614060613cc4565b500690565b5f63ffffffff821663ffffffff8103614080576140806136d8565b60010192915050565b60208101600483106140c2577f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b91905290565b5f82518060208501845e5f92019182525091905056fea2646970667358221220cef29b34a13bc6a389d40170e8413cbe83888570b14fe3e2e4b970871bd681c964736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000010e0000000000000000000000000000000000000000000000000000000000aa36a7
-----Decoded View---------------
Arg [0] : _eraChainId (uint256): 270
Arg [1] : _l1ChainId (uint256): 11155111
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000000000010e
Arg [1] : 0000000000000000000000000000000000000000000000000000000000aa36a7
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.