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
|
||
|---|---|---|---|---|---|---|---|
| 0x60e06040 | 8975410 | 114 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
L1Nullifier
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 {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol";
import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol";
import {IAssetRouterBase, LEGACY_ENCODING_VERSION, NEW_ENCODING_VERSION} from "./asset-router/IAssetRouterBase.sol";
import {IL1NativeTokenVault} from "./ntv/IL1NativeTokenVault.sol";
import {IL1ERC20Bridge} from "./interfaces/IL1ERC20Bridge.sol";
import {IL1AssetRouter} from "./asset-router/IL1AssetRouter.sol";
import {FinalizeL1DepositParams, IL1Nullifier} from "./interfaces/IL1Nullifier.sol";
import {IGetters} from "../state-transition/chain-interfaces/IGetters.sol";
import {IMailboxImpl} from "../state-transition/chain-interfaces/IMailboxImpl.sol";
import {L2Message, TxStatus} from "../common/Messaging.sol";
import {UnsafeBytes} from "../common/libraries/UnsafeBytes.sol";
import {ReentrancyGuard} from "../common/ReentrancyGuard.sol";
import {ETH_TOKEN_ADDRESS} from "../common/Config.sol";
import {DataEncoding} from "../common/libraries/DataEncoding.sol";
import {IBridgehub} from "../bridgehub/IBridgehub.sol";
import {L2_ASSET_ROUTER_ADDR, L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "../common/l2-helpers/L2ContractAddresses.sol";
import {AddressAlreadySet, DepositDoesNotExist, DepositExists, InvalidProof, InvalidSelector, L2WithdrawalMessageWrongLength, LegacyBridgeNotSet, LegacyMethodForNonL1Token, SharedBridgeKey, SharedBridgeValueNotSet, TokenNotLegacy, Unauthorized, WithdrawalAlreadyFinalized, ZeroAddress} from "../common/L1ContractErrors.sol";
import {EthTransferFailed, NativeTokenVaultAlreadySet, WrongL2Sender, WrongMsgLength} from "./L1BridgeContractErrors.sol";
/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @dev Bridges assets between L1 and ZK chain, supporting both ETH and ERC20 tokens.
/// @dev Designed for use with a proxy for upgradability.
contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, PausableUpgradeable {
using SafeERC20 for IERC20;
/// @dev Bridgehub smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication.
IBridgehub public immutable override BRIDGE_HUB;
/// @dev Era's chainID
uint256 internal immutable ERA_CHAIN_ID;
/// @dev The address of ZKsync Era diamond proxy contract.
address internal immutable ERA_DIAMOND_PROXY;
/// @dev Stores the first batch number on the ZKsync Era Diamond Proxy that was settled after Diamond proxy upgrade.
/// This variable is used to differentiate between pre-upgrade and post-upgrade Eth withdrawals. Withdrawals from batches older
/// than this value are considered to have been finalized prior to the upgrade and handled separately.
uint256 internal eraPostDiamondUpgradeFirstBatch;
/// @dev Stores the first batch number on the ZKsync Era Diamond Proxy that was settled after L1ERC20 Bridge upgrade.
/// This variable is used to differentiate between pre-upgrade and post-upgrade ERC20 withdrawals. Withdrawals from batches older
/// than this value are considered to have been finalized prior to the upgrade and handled separately.
uint256 internal eraPostLegacyBridgeUpgradeFirstBatch;
/// @dev Stores the ZKsync Era batch number that processes the last deposit tx initiated by the legacy bridge
/// This variable (together with eraLegacyBridgeLastDepositTxNumber) is used to differentiate between pre-upgrade and post-upgrade deposits. Deposits processed in older batches
/// than this value are considered to have been processed prior to the upgrade and handled separately.
/// We use this both for Eth and erc20 token deposits, so we need to update the diamond and bridge simultaneously.
uint256 internal eraLegacyBridgeLastDepositBatch;
/// @dev The tx number in the _eraLegacyBridgeLastDepositBatch that comes *right after* the last deposit tx initiated by the legacy bridge.
/// This variable (together with eraLegacyBridgeLastDepositBatch) is used to differentiate between pre-upgrade and post-upgrade deposits. Deposits processed in older txs
/// than this value are considered to have been processed prior to the upgrade and handled separately.
/// We use this both for Eth and erc20 token deposits, so we need to update the diamond and bridge simultaneously.
uint256 internal eraLegacyBridgeLastDepositTxNumber;
/// @dev Legacy bridge smart contract that used to hold ERC20 tokens.
IL1ERC20Bridge public override legacyBridge;
/// @dev A mapping chainId => bridgeProxy. Used to store the bridge proxy's address, and to see if it has been deployed yet.
// slither-disable-next-line uninitialized-state
mapping(uint256 chainId => address l2Bridge) public __DEPRECATED_l2BridgeAddress;
/// @dev A mapping chainId => L2 deposit transaction hash => dataHash
// keccak256(abi.encode(account, tokenAddress, amount)) for legacy transfers
// keccak256(abi.encode(_originalCaller, assetId, transferData)) for new transfers
/// @dev Tracks deposit transactions to L2 to enable users to claim their funds if a deposit fails.
mapping(uint256 chainId => mapping(bytes32 l2DepositTxHash => bytes32 depositDataHash))
public
override depositHappened;
/// @dev Tracks the processing status of L2 to L1 messages, indicating whether a message has already been finalized.
mapping(uint256 chainId => mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized)))
public isWithdrawalFinalized;
/// @notice Deprecated. Kept for backwards compatibility.
/// @dev Indicates whether the hyperbridging is enabled for a given chain.
// slither-disable-next-line uninitialized-state
mapping(uint256 chainId => bool enabled) private __DEPRECATED_hyperbridgingEnabled;
/// @dev Maps token balances for each chain to prevent unauthorized spending across ZK chain.
/// This serves as a security measure until hyperbridging is implemented.
/// NOTE: this function may be removed in the future, don't rely on it!
mapping(uint256 chainId => mapping(address l1Token => uint256 balance)) public __DEPRECATED_chainBalance;
/// @dev Admin has the ability to register new chains within the shared bridge.
address public __DEPRECATED_admin;
/// @dev The pending admin, i.e. the candidate to the admin role.
address public __DEPRECATED_pendingAdmin;
/// @dev Address of L1 asset router.
IL1AssetRouter public l1AssetRouter;
/// @dev Address of native token vault.
IL1NativeTokenVault public l1NativeTokenVault;
/// @notice Checks that the message sender is the asset router..
modifier onlyAssetRouter() {
if (msg.sender != address(l1AssetRouter)) {
revert Unauthorized(msg.sender);
}
_;
}
/// @notice Checks that the message sender is the native token vault.
modifier onlyL1NTV() {
if (msg.sender != address(l1NativeTokenVault)) {
revert Unauthorized(msg.sender);
}
_;
}
/// @notice Checks that the message sender is the legacy bridge.
modifier onlyLegacyBridge() {
if (msg.sender != address(legacyBridge)) {
revert Unauthorized(msg.sender);
}
_;
}
/// @dev Contract is expected to be used as proxy implementation.
/// @dev Initialize the implementation to prevent Parity hack.
constructor(IBridgehub _bridgehub, uint256 _eraChainId, address _eraDiamondProxy) reentrancyGuardInitializer {
_disableInitializers();
BRIDGE_HUB = _bridgehub;
ERA_CHAIN_ID = _eraChainId;
ERA_DIAMOND_PROXY = _eraDiamondProxy;
}
/// @dev Initializes a contract bridge for later use. Expected to be used in the proxy.
/// @dev Used for testing purposes only, as the contract has been initialized on mainnet.
/// @param _owner The address which can change L2 token implementation and upgrade the bridge implementation.
/// The owner is the Governor and separate from the ProxyAdmin from now on, so that the Governor can call the bridge.
/// @param _eraPostDiamondUpgradeFirstBatch The first batch number on the ZKsync Era Diamond Proxy that was settled after diamond proxy upgrade.
/// @param _eraPostLegacyBridgeUpgradeFirstBatch The first batch number on the ZKsync Era Diamond Proxy that was settled after legacy bridge upgrade.
/// @param _eraLegacyBridgeLastDepositBatch The the ZKsync Era batch number that processes the last deposit tx initiated by the legacy bridge.
/// @param _eraLegacyBridgeLastDepositTxNumber The tx number in the _eraLegacyBridgeLastDepositBatch of the last deposit tx initiated by the legacy bridge.
function initialize(
address _owner,
uint256 _eraPostDiamondUpgradeFirstBatch,
uint256 _eraPostLegacyBridgeUpgradeFirstBatch,
uint256 _eraLegacyBridgeLastDepositBatch,
uint256 _eraLegacyBridgeLastDepositTxNumber
) external reentrancyGuardInitializer initializer {
if (_owner == address(0)) {
revert ZeroAddress();
}
_transferOwnership(_owner);
if (eraPostDiamondUpgradeFirstBatch == 0) {
eraPostDiamondUpgradeFirstBatch = _eraPostDiamondUpgradeFirstBatch;
eraPostLegacyBridgeUpgradeFirstBatch = _eraPostLegacyBridgeUpgradeFirstBatch;
eraLegacyBridgeLastDepositBatch = _eraLegacyBridgeLastDepositBatch;
eraLegacyBridgeLastDepositTxNumber = _eraLegacyBridgeLastDepositTxNumber;
}
}
/// @notice Transfers tokens from shared bridge to native token vault.
/// @dev This function is part of the upgrade process used to transfer liquidity.
/// @param _token The address of the token to be transferred to NTV.
function transferTokenToNTV(address _token) external onlyL1NTV {
address ntvAddress = address(l1NativeTokenVault);
if (ETH_TOKEN_ADDRESS == _token) {
uint256 amount = address(this).balance;
bool callSuccess;
// Low-level assembly call, to avoid any memory copying (save gas)
assembly {
callSuccess := call(gas(), ntvAddress, amount, 0, 0, 0, 0)
}
if (!callSuccess) {
revert EthTransferFailed();
}
} else {
IERC20(_token).safeTransfer(ntvAddress, IERC20(_token).balanceOf(address(this)));
}
}
/// @notice Clears chain balance for specific token.
/// @dev This function is part of the upgrade process used to nullify chain balances once they are credited to NTV.
/// @param _chainId The ID of the ZK chain.
/// @param _token The address of the token which was previously deposit to shared bridge.
function nullifyChainBalanceByNTV(uint256 _chainId, address _token) external onlyL1NTV {
__DEPRECATED_chainBalance[_chainId][_token] = 0;
}
/// @notice Legacy function used for migration, do not use!
/// @param _chainId The chain id on which the bridge is deployed.
// slither-disable-next-line uninitialized-state-variables
function l2BridgeAddress(uint256 _chainId) external view returns (address) {
// slither-disable-next-line uninitialized-state-variables
return __DEPRECATED_l2BridgeAddress[_chainId];
}
/// @notice Legacy function used for migration, do not use!
/// @param _chainId The chain id we want to get the balance for.
/// @param _token The address of the token.
// slither-disable-next-line uninitialized-state-variables
function chainBalance(uint256 _chainId, address _token) external view returns (uint256) {
// slither-disable-next-line uninitialized-state-variables
return __DEPRECATED_chainBalance[_chainId][_token];
}
/// @notice Sets the L1ERC20Bridge contract address.
/// @dev Should be called only once by the owner.
/// @param _legacyBridge The address of the legacy bridge.
function setL1Erc20Bridge(IL1ERC20Bridge _legacyBridge) external onlyOwner {
if (address(legacyBridge) != address(0)) {
revert AddressAlreadySet(address(legacyBridge));
}
if (address(_legacyBridge) == address(0)) {
revert ZeroAddress();
}
legacyBridge = _legacyBridge;
}
/// @notice Sets the nativeTokenVault contract address.
/// @dev Should be called only once by the owner.
/// @param _l1NativeTokenVault The address of the native token vault.
function setL1NativeTokenVault(IL1NativeTokenVault _l1NativeTokenVault) external onlyOwner {
if (address(l1NativeTokenVault) != address(0)) {
revert NativeTokenVaultAlreadySet();
}
if (address(_l1NativeTokenVault) == address(0)) {
revert ZeroAddress();
}
l1NativeTokenVault = _l1NativeTokenVault;
}
/// @notice Sets the L1 asset router contract address.
/// @dev Should be called only once by the owner.
/// @param _l1AssetRouter The address of the asset router.
function setL1AssetRouter(address _l1AssetRouter) external onlyOwner {
if (address(l1AssetRouter) != address(0)) {
revert AddressAlreadySet(address(l1AssetRouter));
}
if (_l1AssetRouter == address(0)) {
revert ZeroAddress();
}
l1AssetRouter = IL1AssetRouter(_l1AssetRouter);
}
/// @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 bridgehubConfirmL2TransactionForwarded(
uint256 _chainId,
bytes32 _txDataHash,
bytes32 _txHash
) external override onlyAssetRouter whenNotPaused {
if (depositHappened[_chainId][_txHash] != 0x00) {
revert DepositExists();
}
depositHappened[_chainId][_txHash] = _txDataHash;
emit BridgehubDepositFinalized(_chainId, _txDataHash, _txHash);
}
/// @dev Calls the library `encodeTxDataHash`. Used as a wrapped for try / catch case.
/// @dev Encodes the transaction data hash using either the latest encoding standard or the legacy standard.
/// @param _encodingVersion EncodingVersion.
/// @param _originalCaller The address of the entity that initiated the deposit.
/// @param _assetId The unique identifier of the deposited L1 token.
/// @param _transferData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver.
/// @return txDataHash The resulting encoded transaction data hash.
function encodeTxDataHash(
bytes1 _encodingVersion,
address _originalCaller,
bytes32 _assetId,
bytes calldata _transferData
) external view returns (bytes32 txDataHash) {
txDataHash = DataEncoding.encodeTxDataHash({
_encodingVersion: _encodingVersion,
_originalCaller: _originalCaller,
_assetId: _assetId,
_nativeTokenVault: address(l1NativeTokenVault),
_transferData: _transferData
});
}
/// @inheritdoc IL1Nullifier
function bridgeRecoverFailedTransfer(
uint256 _chainId,
address _depositSender,
bytes32 _assetId,
bytes memory _assetData,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) public nonReentrant {
_verifyAndClearFailedTransfer({
_checkedInLegacyBridge: false,
_chainId: _chainId,
_depositSender: _depositSender,
_assetId: _assetId,
_assetData: _assetData,
_l2TxHash: _l2TxHash,
_l2BatchNumber: _l2BatchNumber,
_l2MessageIndex: _l2MessageIndex,
_l2TxNumberInBatch: _l2TxNumberInBatch,
_merkleProof: _merkleProof
});
l1AssetRouter.bridgeRecoverFailedTransfer(_chainId, _depositSender, _assetId, _assetData);
}
/// @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 data, which is used by the asset handler to determine L2 recipient and amount. 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 _verifyAndClearFailedTransfer(
bool _checkedInLegacyBridge,
uint256 _chainId,
address _depositSender,
bytes32 _assetId,
bytes memory _assetData,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) internal whenNotPaused {
{
bool proofValid = BRIDGE_HUB.proveL1ToL2TransactionStatus({
_chainId: _chainId,
_l2TxHash: _l2TxHash,
_l2BatchNumber: _l2BatchNumber,
_l2MessageIndex: _l2MessageIndex,
_l2TxNumberInBatch: _l2TxNumberInBatch,
_merkleProof: _merkleProof,
_status: TxStatus.Failure
});
if (!proofValid) {
revert InvalidProof();
}
}
bool notCheckedInLegacyBridgeOrWeCanCheckDeposit;
{
// Deposits that happened before the upgrade cannot be checked here, they have to be claimed and checked in the legacyBridge
bool weCanCheckDepositHere = !_isPreSharedBridgeDepositOnEra(_chainId, _l2BatchNumber, _l2TxNumberInBatch);
// Double claims are not possible, as depositHappened is checked here for all except legacy deposits (which have to happen through the legacy bridge)
// Funds claimed before the update will still be recorded in the legacy bridge
// Note we double check NEW deposits if they are called from the legacy bridge
notCheckedInLegacyBridgeOrWeCanCheckDeposit = (!_checkedInLegacyBridge) || weCanCheckDepositHere;
}
if (notCheckedInLegacyBridgeOrWeCanCheckDeposit) {
bytes32 dataHash = depositHappened[_chainId][_l2TxHash];
// Determine if the given dataHash matches the calculated legacy transaction hash.
bool isLegacyTxDataHash = _isLegacyTxDataHash(_depositSender, _assetId, _assetData, dataHash);
// If the dataHash matches the legacy transaction hash, skip the next step.
// Otherwise, perform the check using the new transaction data hash encoding.
if (!isLegacyTxDataHash) {
bytes32 txDataHash = DataEncoding.encodeTxDataHash({
_encodingVersion: NEW_ENCODING_VERSION,
_originalCaller: _depositSender,
_assetId: _assetId,
_nativeTokenVault: address(l1NativeTokenVault),
_transferData: _assetData
});
if (dataHash != txDataHash) {
revert DepositDoesNotExist();
}
}
}
delete depositHappened[_chainId][_l2TxHash];
}
/// @notice Finalize the withdrawal and release funds.
/// @param _finalizeWithdrawalParams The structure that holds all necessary data to finalize withdrawal
/// @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(FinalizeL1DepositParams memory _finalizeWithdrawalParams) public {
_finalizeDeposit(_finalizeWithdrawalParams);
}
/// @notice Internal function that handles the logic for finalizing withdrawals, supporting both the current bridge system and the legacy ERC20 bridge.
/// @param _finalizeWithdrawalParams The structure that holds all necessary data to finalize withdrawal
function _finalizeDeposit(
FinalizeL1DepositParams memory _finalizeWithdrawalParams
) internal nonReentrant whenNotPaused {
uint256 chainId = _finalizeWithdrawalParams.chainId;
uint256 l2BatchNumber = _finalizeWithdrawalParams.l2BatchNumber;
uint256 l2MessageIndex = _finalizeWithdrawalParams.l2MessageIndex;
if (isWithdrawalFinalized[chainId][l2BatchNumber][l2MessageIndex]) {
revert WithdrawalAlreadyFinalized();
}
isWithdrawalFinalized[chainId][l2BatchNumber][l2MessageIndex] = true;
(bytes32 assetId, bytes memory transferData) = _verifyWithdrawal(_finalizeWithdrawalParams);
// Handling special case for withdrawal from ZKsync Era initiated before Shared Bridge.
if (_isPreSharedBridgeEraEthWithdrawal(chainId, l2BatchNumber)) {
// Checks that the withdrawal wasn't finalized already.
bool alreadyFinalized = IGetters(ERA_DIAMOND_PROXY).isEthWithdrawalFinalized(l2BatchNumber, l2MessageIndex);
if (alreadyFinalized) {
revert WithdrawalAlreadyFinalized();
}
}
if (_isPreSharedBridgeEraTokenWithdrawal(chainId, l2BatchNumber)) {
if (legacyBridge.isWithdrawalFinalized(l2BatchNumber, l2MessageIndex)) {
revert WithdrawalAlreadyFinalized();
}
}
l1AssetRouter.finalizeDeposit(chainId, assetId, transferData);
}
/// @dev Determines if an eth withdrawal was initiated on ZKsync Era before the upgrade to the Shared Bridge.
/// @param _chainId The chain ID of the transaction to check.
/// @param _l2BatchNumber The L2 batch number for the withdrawal.
/// @return Whether withdrawal was initiated on ZKsync Era before diamond proxy upgrade.
function _isPreSharedBridgeEraEthWithdrawal(uint256 _chainId, uint256 _l2BatchNumber) internal view returns (bool) {
if ((_chainId == ERA_CHAIN_ID) && eraPostDiamondUpgradeFirstBatch == 0) {
revert SharedBridgeValueNotSet(SharedBridgeKey.PostUpgradeFirstBatch);
}
return (_chainId == ERA_CHAIN_ID) && (_l2BatchNumber < eraPostDiamondUpgradeFirstBatch);
}
/// @dev Determines if a token withdrawal was initiated on ZKsync Era before the upgrade to the Shared Bridge.
/// @param _chainId The chain ID of the transaction to check.
/// @param _l2BatchNumber The L2 batch number for the withdrawal.
/// @return Whether withdrawal was initiated on ZKsync Era before Legacy Bridge upgrade.
function _isPreSharedBridgeEraTokenWithdrawal(
uint256 _chainId,
uint256 _l2BatchNumber
) internal view returns (bool) {
if ((_chainId == ERA_CHAIN_ID) && eraPostLegacyBridgeUpgradeFirstBatch == 0) {
revert SharedBridgeValueNotSet(SharedBridgeKey.LegacyBridgeFirstBatch);
}
return (_chainId == ERA_CHAIN_ID) && (_l2BatchNumber < eraPostLegacyBridgeUpgradeFirstBatch);
}
/// @dev Determines if the provided data for a failed deposit corresponds to a legacy failed deposit.
/// @param _depositSender The address of the entity that initiated the deposit.
/// @param _assetId The unique identifier of the deposited L1 token.
/// @param _transferData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver.
/// @param _expectedTxDataHash The nullifier data hash stored for the failed deposit.
/// @return isLegacyTxDataHash True if the transaction is legacy, false otherwise.
function _isLegacyTxDataHash(
address _depositSender,
bytes32 _assetId,
bytes memory _transferData,
bytes32 _expectedTxDataHash
) internal view returns (bool isLegacyTxDataHash) {
try this.encodeTxDataHash(LEGACY_ENCODING_VERSION, _depositSender, _assetId, _transferData) returns (
bytes32 txDataHash
) {
return txDataHash == _expectedTxDataHash;
} catch {
return false;
}
}
/// @dev Determines if a deposit was initiated on ZKsync Era before the upgrade to the Shared Bridge.
/// @param _chainId The chain ID of the transaction to check.
/// @param _l2BatchNumber The L2 batch number for the deposit where it was processed.
/// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the deposit was processed.
/// @return Whether deposit was initiated on ZKsync Era before Shared Bridge upgrade.
function _isPreSharedBridgeDepositOnEra(
uint256 _chainId,
uint256 _l2BatchNumber,
uint256 _l2TxNumberInBatch
) internal view returns (bool) {
if ((_chainId == ERA_CHAIN_ID) && (eraLegacyBridgeLastDepositBatch == 0)) {
revert SharedBridgeValueNotSet(SharedBridgeKey.LegacyBridgeLastDepositBatch);
}
return
(_chainId == ERA_CHAIN_ID) &&
(_l2BatchNumber < eraLegacyBridgeLastDepositBatch ||
(_l2TxNumberInBatch < eraLegacyBridgeLastDepositTxNumber &&
_l2BatchNumber == eraLegacyBridgeLastDepositBatch));
}
/// @notice Verifies the validity of a withdrawal message from L2 and returns withdrawal details.
/// @param _finalizeWithdrawalParams The structure that holds all necessary data to finalize withdrawal
/// @return assetId The ID of the bridged asset.
/// @return transferData The transfer data used to finalize withdawal.
function _verifyWithdrawal(
FinalizeL1DepositParams memory _finalizeWithdrawalParams
) internal returns (bytes32 assetId, bytes memory transferData) {
(assetId, transferData) = _parseL2WithdrawalMessage(
_finalizeWithdrawalParams.chainId,
_finalizeWithdrawalParams.message
);
L2Message memory l2ToL1Message;
{
address l2Sender = _finalizeWithdrawalParams.l2Sender;
bool baseTokenWithdrawal = (assetId == BRIDGE_HUB.baseTokenAssetId(_finalizeWithdrawalParams.chainId));
bool isL2SenderCorrect = l2Sender == L2_ASSET_ROUTER_ADDR ||
l2Sender == L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR ||
l2Sender == __DEPRECATED_l2BridgeAddress[_finalizeWithdrawalParams.chainId];
if (!isL2SenderCorrect) {
revert WrongL2Sender(l2Sender);
}
l2ToL1Message = L2Message({
txNumberInBatch: _finalizeWithdrawalParams.l2TxNumberInBatch,
sender: baseTokenWithdrawal ? L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR : l2Sender,
data: _finalizeWithdrawalParams.message
});
}
bool success = BRIDGE_HUB.proveL2MessageInclusion({
_chainId: _finalizeWithdrawalParams.chainId,
_batchNumber: _finalizeWithdrawalParams.l2BatchNumber,
_index: _finalizeWithdrawalParams.l2MessageIndex,
_message: l2ToL1Message,
_proof: _finalizeWithdrawalParams.merkleProof
});
// withdrawal wrong proof
if (!success) {
revert InvalidProof();
}
}
/// @notice Parses the withdrawal message and returns withdrawal details.
/// @dev Currently, 3 different encoding versions are supported: legacy mailbox withdrawal, ERC20 bridge withdrawal,
/// @dev and the latest version supported by shared bridge. Selectors are used for versioning.
/// @param _chainId The ZK chain ID.
/// @param _l2ToL1message The encoded L2 -> L1 message.
/// @return assetId The ID of the bridged asset.
/// @return transferData The transfer data used to finalize withdawal.
function _parseL2WithdrawalMessage(
uint256 _chainId,
bytes memory _l2ToL1message
) internal returns (bytes32 assetId, bytes memory transferData) {
// Please note that there are three versions of the message:
// 1. The message that is sent from `L2BaseToken` to withdraw base token.
// 2. The message that is sent from L2 Legacy Shared Bridge to withdraw ERC20 tokens or base token.
// 3. The message that is sent from L2 Asset Router to withdraw ERC20 tokens or base token.
uint256 amount;
address l1Receiver;
(uint32 functionSignature, uint256 offset) = UnsafeBytes.readUint32(_l2ToL1message, 0);
if (bytes4(functionSignature) == IMailboxImpl.finalizeEthWithdrawal.selector) {
// The data is expected to be at least 56 bytes long.
if (_l2ToL1message.length < 56) {
revert L2WithdrawalMessageWrongLength(_l2ToL1message.length);
}
// this message is a base token withdrawal
(l1Receiver, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset);
// slither-disable-next-line unused-return
(amount, ) = UnsafeBytes.readUint256(_l2ToL1message, offset);
assetId = BRIDGE_HUB.baseTokenAssetId(_chainId);
transferData = DataEncoding.encodeBridgeMintData({
_originalCaller: address(0),
_remoteReceiver: l1Receiver,
// Note, that `assetId` could belong to a token native to an L2, and so
// the logic for determining the correct origin token address will be complex.
// It is expected that this value won't be used in the NativeTokenVault and so providing
// any value is acceptable here.
_originToken: address(0),
_amount: amount,
_erc20Metadata: new bytes(0)
});
} else if (bytes4(functionSignature) == IL1ERC20Bridge.finalizeWithdrawal.selector) {
// this message is a token withdrawal
// Check that the message length is correct.
// It should be equal to the length of the function signature + address + address + uint256 = 4 + 20 + 20 + 32 =
// 76 (bytes).
if (_l2ToL1message.length != 76) {
revert L2WithdrawalMessageWrongLength(_l2ToL1message.length);
}
(l1Receiver, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset);
// We use the IL1ERC20Bridge for backward compatibility with old withdrawals.
address l1Token;
(l1Token, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset);
// slither-disable-next-line unused-return
(amount, ) = UnsafeBytes.readUint256(_l2ToL1message, offset);
assetId = l1NativeTokenVault.ensureTokenIsRegistered(l1Token);
bytes32 expectedAssetId = DataEncoding.encodeNTVAssetId(block.chainid, l1Token);
// This method is only expected to use L1-based tokens.
if (assetId != expectedAssetId) {
revert TokenNotLegacy();
}
transferData = DataEncoding.encodeBridgeMintData({
_originalCaller: address(0),
_remoteReceiver: l1Receiver,
_originToken: l1Token,
_amount: amount,
_erc20Metadata: new bytes(0)
});
} else if (bytes4(functionSignature) == IAssetRouterBase.finalizeDeposit.selector) {
// The data is expected to be at least 68 bytes long to contain assetId.
if (_l2ToL1message.length < 68) {
revert WrongMsgLength(68, _l2ToL1message.length);
}
// slither-disable-next-line unused-return
(, offset) = UnsafeBytes.readUint256(_l2ToL1message, offset); // originChainId, not used for L2->L1 txs
(assetId, offset) = UnsafeBytes.readBytes32(_l2ToL1message, offset);
transferData = UnsafeBytes.readRemainingBytes(_l2ToL1message, offset);
} else {
revert InvalidSelector(bytes4(functionSignature));
}
}
/*//////////////////////////////////////////////////////////////
SHARED BRIDGE TOKEN BRIDGING LEGACY FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2.
/// @param _depositSender The address of the deposit initiator.
/// @param _l1Token The address of the deposited L1 ERC20 token.
/// @param _amount The amount of the deposit that failed.
/// @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.
function claimFailedDeposit(
uint256 _chainId,
address _depositSender,
address _l1Token,
uint256 _amount,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external {
bytes32 assetId = l1NativeTokenVault.assetId(_l1Token);
bytes32 ntvAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _l1Token);
if (assetId == bytes32(0)) {
assetId = ntvAssetId;
} else if (assetId != ntvAssetId) {
revert LegacyMethodForNonL1Token();
}
// For legacy deposits, the l2 receiver is not required to check tx data hash
// The token address does not have to be provided for this functionality either.
bytes memory assetData = DataEncoding.encodeBridgeBurnData(_amount, address(0), address(0));
_verifyAndClearFailedTransfer({
_checkedInLegacyBridge: false,
_depositSender: _depositSender,
_chainId: _chainId,
_assetId: assetId,
_assetData: assetData,
_l2TxHash: _l2TxHash,
_l2BatchNumber: _l2BatchNumber,
_l2MessageIndex: _l2MessageIndex,
_l2TxNumberInBatch: _l2TxNumberInBatch,
_merkleProof: _merkleProof
});
l1AssetRouter.bridgeRecoverFailedTransfer({
_chainId: _chainId,
_depositSender: _depositSender,
_assetId: assetId,
_assetData: assetData
});
}
/*//////////////////////////////////////////////////////////////
ERA ERC20 LEGACY FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @notice Withdraw funds from the initiated deposit, that failed when finalizing on ZKsync Era chain.
/// This function is specifically designed for maintaining backward-compatibility with legacy `claimFailedDeposit`
/// method in `L1ERC20Bridge`.
///
/// @param _depositSender The address of the deposit initiator.
/// @param _l1Token The address of the deposited L1 ERC20 token.
/// @param _amount The amount of the deposit that failed.
/// @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.
function claimFailedDepositLegacyErc20Bridge(
address _depositSender,
address _l1Token,
uint256 _amount,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external override onlyLegacyBridge {
// For legacy deposits, the l2 receiver is not required to check tx data hash
// The token address does not have to be provided for this functionality either.
bytes memory assetData = DataEncoding.encodeBridgeBurnData(_amount, address(0), address(0));
/// the legacy bridge can only be used with L1 native tokens.
bytes32 assetId = DataEncoding.encodeNTVAssetId(block.chainid, _l1Token);
_verifyAndClearFailedTransfer({
_checkedInLegacyBridge: true,
_depositSender: _depositSender,
_chainId: ERA_CHAIN_ID,
_assetId: assetId,
_assetData: assetData,
_l2TxHash: _l2TxHash,
_l2BatchNumber: _l2BatchNumber,
_l2MessageIndex: _l2MessageIndex,
_l2TxNumberInBatch: _l2TxNumberInBatch,
_merkleProof: _merkleProof
});
l1AssetRouter.bridgeRecoverFailedTransfer({
_chainId: ERA_CHAIN_ID,
_depositSender: _depositSender,
_assetId: assetId,
_assetData: assetData
});
}
/*//////////////////////////////////////////////////////////////
PAUSE
//////////////////////////////////////////////////////////////*/
/// @notice Pauses all functions marked with the `whenNotPaused` modifier.
function pause() external onlyOwner {
_pause();
}
/// @notice Unpauses the contract, allowing all functions marked with the `whenNotPaused` modifier to be called again.
function unpause() external onlyOwner {
_unpause();
}
/*//////////////////////////////////////////////////////////////
LEGACY INTERFACE
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IL1Nullifier
function finalizeWithdrawal(
uint256 _chainId,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes calldata _message,
bytes32[] calldata _merkleProof
) external override {
/// @dev We use a deprecated field to support L2->L1 legacy withdrawals, which were started
/// by the legacy bridge.
address legacyL2Bridge = __DEPRECATED_l2BridgeAddress[_chainId];
if (legacyL2Bridge == address(0)) {
revert LegacyBridgeNotSet();
}
FinalizeL1DepositParams memory finalizeWithdrawalParams = FinalizeL1DepositParams({
chainId: _chainId,
l2BatchNumber: _l2BatchNumber,
l2MessageIndex: _l2MessageIndex,
l2Sender: legacyL2Bridge,
l2TxNumberInBatch: _l2TxNumberInBatch,
message: _message,
merkleProof: _merkleProof
});
finalizeDeposit(finalizeWithdrawalParams);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./OwnableUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
function __Ownable2Step_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable2Step_init_unchained() internal onlyInitializing {
}
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
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;
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
// 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
// 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.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
// 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 {VerifierParams} from "../chain-interfaces/IVerifier.sol";
import {PubdataPricingMode} from "../chain-deps/ZKChainStorage.sol";
import {IZKChainBase} from "./IZKChainBase.sol";
/// @title The interface of the Getters Contract that implements functions for getting contract state from outside the blockchain.
/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @dev Most of the methods simply return the values that correspond to the current diamond proxy and possibly
/// not to the ZK Chain as a whole. For example, if the chain is migrated to another settlement layer, the values returned
/// by this facet will correspond to the values stored on this chain and possilbly not the canonical state of the chain.
interface IGetters is IZKChainBase {
/*//////////////////////////////////////////////////////////////
CUSTOM GETTERS
//////////////////////////////////////////////////////////////*/
/// @return The address of the verifier smart contract
function getVerifier() external view returns (address);
/// @return The address of the current admin
function getAdmin() external view returns (address);
/// @return The address of the pending admin
function getPendingAdmin() external view returns (address);
/// @return The address of the bridgehub
function getBridgehub() external view returns (address);
/// @return The address of the state transition
function getChainTypeManager() external view returns (address);
/// @return The chain ID
function getChainId() external view returns (uint256);
/// @return The address of the base token
function getBaseToken() external view returns (address);
/// @return The address of the base token
function getBaseTokenAssetId() external view returns (bytes32);
/// @return The total number of batches that were committed
function getTotalBatchesCommitted() external view returns (uint256);
/// @return The total number of batches that were committed & verified
function getTotalBatchesVerified() external view returns (uint256);
/// @return The total number of batches that were committed & verified & executed
function getTotalBatchesExecuted() external view returns (uint256);
// @return Address of transaction filterer
function getTransactionFilterer() external view returns (address);
/// @return The total number of priority operations that were added to the priority queue, including all processed ones
function getTotalPriorityTxs() external view returns (uint256);
/// @return The start index of the priority tree, i.e. the index of the first priority operation that
/// was included into the priority tree.
function getPriorityTreeStartIndex() external view returns (uint256);
/// @return The root hash of the priority tree
function getPriorityTreeRoot() external view returns (bytes32);
/// @return Whether the priority queue is active, i.e. whether new priority operations are appended to it.
/// Once the chain processes all the transactions that were present in the priority queue, all the L1->L2 related
/// operations will start to get done using the priority tree.
function isPriorityQueueActive() external view returns (bool);
/// @notice The function that returns the first unprocessed priority transaction.
/// @dev Returns zero if and only if no operations were processed from the queue.
/// @dev If all the transactions were processed, it will return the last processed index, so
/// in case exactly *unprocessed* transactions are needed, one should check that getPriorityQueueSize() is greater than 0.
/// @return Index of the oldest priority operation that wasn't processed yet
function getFirstUnprocessedPriorityTx() external view returns (uint256);
/// @return The number of priority operations currently in the queue
function getPriorityQueueSize() external view returns (uint256);
/// @return Whether the address has a validator access
function isValidator(address _address) external view returns (bool);
/// @return merkleRoot Merkle root of the tree with L2 logs for the selected batch
function l2LogsRootHash(uint256 _batchNumber) external view returns (bytes32 merkleRoot);
/// @notice For unfinalized (non executed) batches may change
/// @dev returns zero for non-committed batches
/// @return The hash of committed L2 batch.
function storedBatchHash(uint256 _batchNumber) external view returns (bytes32);
/// @return Bytecode hash of bootloader program.
function getL2BootloaderBytecodeHash() external view returns (bytes32);
/// @return Bytecode hash of default account (bytecode for EOA).
function getL2DefaultAccountBytecodeHash() external view returns (bytes32);
/// @return Bytecode hash of EVM emulator.
function getL2EvmEmulatorBytecodeHash() external view returns (bytes32);
/// @return Verifier parameters.
/// @dev This function is deprecated and will soon be removed.
function getVerifierParams() external view returns (VerifierParams memory);
/// @return Whether the diamond is frozen or not
function isDiamondStorageFrozen() external view returns (bool);
/// @return The current packed protocol version. To access human-readable version, use `getSemverProtocolVersion` function.
function getProtocolVersion() external view returns (uint256);
/// @return The tuple of (major, minor, patch) protocol version.
function getSemverProtocolVersion() external view returns (uint32, uint32, uint32);
/// @return The upgrade system contract transaction hash, 0 if the upgrade is not initialized
function getL2SystemContractsUpgradeTxHash() external view returns (bytes32);
/// @return The L2 batch number in which the upgrade transaction was processed.
/// @dev It is equal to 0 in the following two cases:
/// - No upgrade transaction has ever been processed.
/// - The upgrade transaction has been processed and the batch with such transaction has been
/// executed (i.e. finalized).
function getL2SystemContractsUpgradeBatchNumber() external view returns (uint256);
/// @return The maximum number of L2 gas that a user can request for L1 -> L2 transactions
function getPriorityTxMaxGasLimit() external view returns (uint256);
/// @return Whether a withdrawal has been finalized.
/// @param _l2BatchNumber The L2 batch number within which the withdrawal happened.
/// @param _l2MessageIndex The index of the L2->L1 message denoting the withdrawal.
function isEthWithdrawalFinalized(uint256 _l2BatchNumber, uint256 _l2MessageIndex) external view returns (bool);
/// @return The pubdata pricing mode.
function getPubdataPricingMode() external view returns (PubdataPricingMode);
/// @return the baseTokenGasPriceMultiplierNominator, used to compare the baseTokenPrice to ether for L1->L2 transactions
function baseTokenGasPriceMultiplierNominator() external view returns (uint128);
/// @return the baseTokenGasPriceMultiplierDenominator, used to compare the baseTokenPrice to ether for L1->L2 transactions
function baseTokenGasPriceMultiplierDenominator() external view returns (uint128);
/*//////////////////////////////////////////////////////////////
DIAMOND LOUPE
//////////////////////////////////////////////////////////////*/
/// @notice Faсet structure compatible with the EIP-2535 diamond loupe
/// @param addr The address of the facet contract
/// @param selectors The NON-sorted array with selectors associated with facet
struct Facet {
address addr;
bytes4[] selectors;
}
/// @return result All facet addresses and their function selectors
function facets() external view returns (Facet[] memory);
/// @return NON-sorted array with function selectors supported by a specific facet
function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory);
/// @return facets NON-sorted array of facet addresses supported on diamond
function facetAddresses() external view returns (address[] memory facets);
/// @return facet The facet address associated with a selector. Zero if the selector is not added to the diamond
function facetAddress(bytes4 _selector) external view returns (address facet);
/// @return Whether the selector can be frozen by the admin or always accessible
function isFunctionFreezable(bytes4 _selector) external view returns (bool);
/// @return isFreezable Whether the facet can be frozen by the admin or always accessible
function isFacetFreezable(address _facet) external view returns (bool isFreezable);
/// @return The address of the current settlement layer.
function getSettlementLayer() 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 {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 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;
/// @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 // 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] * @dev The library provides a set of functions that help read data from an "abi.encodePacked" byte array. * @dev Each of the functions accepts the `bytes memory` and the offset where data should be read and returns a value of a certain type. * * @dev WARNING! * 1) Functions don't check the length of the bytes array, so it can go out of bounds. * The user of the library must check for bytes length before using any functions from the library! * * 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html. * Using data in inline assembly can lead to unexpected behavior! */ library UnsafeBytes { function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) { assembly { offset := add(_start, 4) result := mload(add(_bytes, offset)) } } function readAddress(bytes memory _bytes, uint256 _start) internal pure returns (address result, uint256 offset) { assembly { offset := add(_start, 20) result := mload(add(_bytes, offset)) } } function readUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result, uint256 offset) { assembly { offset := add(_start, 32) result := mload(add(_bytes, offset)) } } function readBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result, uint256 offset) { assembly { offset := add(_start, 32) result := mload(add(_bytes, offset)) } } function readRemainingBytes(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory result) { uint256 arrayLen = _bytes.length - _start; result = new bytes(arrayLen); assembly { mcopy(add(result, 0x20), add(_bytes, add(0x20, _start)), arrayLen) } } }
// 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.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
pragma solidity 0.8.28;
import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../l2-helpers/L2ContractAddresses.sol";
import {LEGACY_ENCODING_VERSION, NEW_ENCODING_VERSION} from "../../bridge/asset-router/IAssetRouterBase.sol";
import {INativeTokenVault} from "../../bridge/ntv/INativeTokenVault.sol";
import {IncorrectTokenAddressFromNTV, InvalidNTVBurnData, UnsupportedEncodingVersion} from "../L1ContractErrors.sol";
/**
* @author Matter Labs
* @custom:security-contact [email protected]
* @notice Helper library for transfer data encoding and decoding to reduce possibility of errors.
*/
library DataEncoding {
/// @notice Abi.encodes the data required for bridgeBurn for NativeTokenVault.
/// @param _amount The amount of token to be transferred.
/// @param _remoteReceiver The address which to receive tokens on remote chain.
/// @param _maybeTokenAddress The helper field that should be either equal to 0 (in this case
/// it is assumed that the token has been registered within NativeTokenVault already) or it
/// can be equal to the address of the token on the current chain. Providing non-zero address
/// allows it to be automatically registered in case it is not yet a part of NativeTokenVault.
/// @return The encoded bridgeBurn data
function encodeBridgeBurnData(
uint256 _amount,
address _remoteReceiver,
address _maybeTokenAddress
) internal pure returns (bytes memory) {
return abi.encode(_amount, _remoteReceiver, _maybeTokenAddress);
}
/// @notice Function decoding bridgeBurn data previously encoded with this library.
/// @param _data The encoded data for bridgeBurn
/// @return amount The amount of token to be transferred.
/// @return receiver The address which to receive tokens on remote chain.
/// @return maybeTokenAddress The helper field that should be either equal to 0 (in this case
/// it is assumed that the token has been registered within NativeTokenVault already) or it
/// can be equal to the address of the token on the current chain. Providing non-zero address
/// allows it to be automatically registered in case it is not yet a part of NativeTokenVault.
function decodeBridgeBurnData(
bytes memory _data
) internal pure returns (uint256 amount, address receiver, address maybeTokenAddress) {
if (_data.length != 96) {
// For better error handling
revert InvalidNTVBurnData();
}
(amount, receiver, maybeTokenAddress) = abi.decode(_data, (uint256, address, address));
}
/// @notice Abi.encodes the data required for bridgeMint on remote chain.
/// @param _originalCaller The address which initiated the transfer.
/// @param _remoteReceiver The address which to receive tokens on remote chain.
/// @param _originToken The transferred token address.
/// @param _amount The amount of token to be transferred.
/// @param _erc20Metadata The transferred token metadata.
/// @return The encoded bridgeMint data
function encodeBridgeMintData(
address _originalCaller,
address _remoteReceiver,
address _originToken,
uint256 _amount,
bytes memory _erc20Metadata
) internal pure returns (bytes memory) {
// solhint-disable-next-line func-named-parameters
return abi.encode(_originalCaller, _remoteReceiver, _originToken, _amount, _erc20Metadata);
}
/// @notice Function decoding transfer data previously encoded with this library.
/// @param _bridgeMintData The encoded bridgeMint data
/// @return _originalCaller The address which initiated the transfer.
/// @return _remoteReceiver The address which to receive tokens on remote chain.
/// @return _parsedOriginToken The transferred token address.
/// @return _amount The amount of token to be transferred.
/// @return _erc20Metadata The transferred token metadata.
function decodeBridgeMintData(
bytes memory _bridgeMintData
)
internal
pure
returns (
address _originalCaller,
address _remoteReceiver,
address _parsedOriginToken,
uint256 _amount,
bytes memory _erc20Metadata
)
{
(_originalCaller, _remoteReceiver, _parsedOriginToken, _amount, _erc20Metadata) = abi.decode(
_bridgeMintData,
(address, address, address, uint256, bytes)
);
}
/// @notice Encodes the asset data by combining chain id, asset deployment tracker and asset data.
/// @param _chainId The id of the chain token is native to.
/// @param _assetData The asset data that has to be encoded.
/// @param _sender The asset deployment tracker address.
/// @return The encoded asset data.
function encodeAssetId(uint256 _chainId, bytes32 _assetData, address _sender) internal pure returns (bytes32) {
return keccak256(abi.encode(_chainId, _sender, _assetData));
}
/// @notice Encodes the asset data by combining chain id, asset deployment tracker and asset data.
/// @param _chainId The id of the chain token is native to.
/// @param _tokenAddress The address of token that has to be encoded (asset data is the address itself).
/// @param _sender The asset deployment tracker address.
/// @return The encoded asset data.
function encodeAssetId(uint256 _chainId, address _tokenAddress, address _sender) internal pure returns (bytes32) {
return keccak256(abi.encode(_chainId, _sender, _tokenAddress));
}
/// @notice Encodes the asset data by combining chain id, NTV as asset deployment tracker and asset data.
/// @param _chainId The id of the chain token is native to.
/// @param _assetData The asset data that has to be encoded.
/// @return The encoded asset data.
function encodeNTVAssetId(uint256 _chainId, bytes32 _assetData) internal pure returns (bytes32) {
return keccak256(abi.encode(_chainId, L2_NATIVE_TOKEN_VAULT_ADDR, _assetData));
}
/// @notice Encodes the asset data by combining chain id, NTV as asset deployment tracker and token address.
/// @param _chainId The id of the chain token is native to.
/// @param _tokenAddress The address of token that has to be encoded (asset data is the address itself).
/// @return The encoded asset data.
function encodeNTVAssetId(uint256 _chainId, address _tokenAddress) internal pure returns (bytes32) {
return keccak256(abi.encode(_chainId, L2_NATIVE_TOKEN_VAULT_ADDR, _tokenAddress));
}
/// @dev Encodes the transaction data hash using either the latest encoding standard or the legacy standard.
/// @param _encodingVersion EncodingVersion.
/// @param _originalCaller The address of the entity that initiated the deposit.
/// @param _assetId The unique identifier of the deposited L1 token.
/// @param _nativeTokenVault The address of the token, only used if the encoding version is legacy.
/// @param _transferData The encoded transfer data, which includes the deposit amount, the address of the L2 receiver, and potentially the token address.
/// @return txDataHash The resulting encoded transaction data hash.
function encodeTxDataHash(
bytes1 _encodingVersion,
address _originalCaller,
bytes32 _assetId,
address _nativeTokenVault,
bytes memory _transferData
) internal view returns (bytes32 txDataHash) {
if (_encodingVersion == LEGACY_ENCODING_VERSION) {
address tokenAddress = INativeTokenVault(_nativeTokenVault).tokenAddress(_assetId);
// This is a double check to ensure that the used token for the legacy encoding is correct.
// This revert should never be emitted and in real life and should only serve as a guard in
// case of inconsistent state of Native Token Vault.
bytes32 expectedAssetId = encodeNTVAssetId(block.chainid, tokenAddress);
if (_assetId != expectedAssetId) {
revert IncorrectTokenAddressFromNTV(_assetId, tokenAddress);
}
(uint256 depositAmount, , ) = decodeBridgeBurnData(_transferData);
txDataHash = keccak256(abi.encode(_originalCaller, tokenAddress, depositAmount));
} else if (_encodingVersion == NEW_ENCODING_VERSION) {
// Similarly to calldata, the txDataHash is collision-resistant.
// In the legacy data hash, the first encoded variable was the address, which is padded with zeros during `abi.encode`.
txDataHash = keccak256(
bytes.concat(_encodingVersion, abi.encode(_originalCaller, _assetId, _transferData))
);
} else {
revert UnsupportedEncodingVersion();
}
}
/// @notice Decodes the token data by combining chain id, asset deployment tracker and asset data.
function decodeTokenData(
bytes calldata _tokenData
) internal pure returns (uint256 chainId, bytes memory name, bytes memory symbol, bytes memory decimals) {
bytes1 encodingVersion = _tokenData[0];
if (encodingVersion == LEGACY_ENCODING_VERSION) {
(name, symbol, decimals) = abi.decode(_tokenData, (bytes, bytes, bytes));
} else if (encodingVersion == NEW_ENCODING_VERSION) {
return abi.decode(_tokenData[1:], (uint256, bytes, bytes, bytes));
} else {
revert UnsupportedEncodingVersion();
}
}
/// @notice Encodes the token data by combining chain id, and its metadata.
/// @dev Note that all the metadata of the token is expected to be ABI encoded.
/// @param _chainId The id of the chain token is native to.
/// @param _name The name of the token.
/// @param _symbol The symbol of the token.
/// @param _decimals The decimals of the token.
/// @return The encoded token data.
function encodeTokenData(
uint256 _chainId,
bytes memory _name,
bytes memory _symbol,
bytes memory _decimals
) internal pure returns (bytes memory) {
return bytes.concat(NEW_ENCODING_VERSION, abi.encode(_chainId, _name, _symbol, _decimals));
}
}// 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;
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
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; // 0x0fef9068 error ClaimFailedDepositFailed(); // 0x066f53b1 error EmptyToken(); // 0x6d963f88 error EthTransferFailed(); // 0x1c55230b error NativeTokenVaultAlreadySet(); // 0xfeda3bf8 error WrongAmountTransferred(uint256 balance, uint256 nullifierChainBalance); // 0xb4aeddbc error WrongCounterpart(); // 0x636c90db error WrongL2Sender(address providedL2Sender); // 0x61cdb17e error WrongMsgLength(uint256 expected, uint256 length); // 0xe4742c42 error ZeroAmountToTransfer();
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
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; /// @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 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;
/// @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
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; /// @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.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
// 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
// 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
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// 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 {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
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 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); // 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
// 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 // 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
// 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
}
}
}// 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);
}
}
}{
"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":"contract IBridgehub","name":"_bridgehub","type":"address"},{"internalType":"uint256","name":"_eraChainId","type":"uint256"},{"internalType":"address","name":"_eraDiamondProxy","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"AddressAlreadySet","type":"error"},{"inputs":[],"name":"DepositDoesNotExist","type":"error"},{"inputs":[],"name":"DepositExists","type":"error"},{"inputs":[],"name":"EthTransferFailed","type":"error"},{"inputs":[{"internalType":"bytes32","name":"assetId","type":"bytes32"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"IncorrectTokenAddressFromNTV","type":"error"},{"inputs":[],"name":"InvalidNTVBurnData","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[{"internalType":"bytes4","name":"func","type":"bytes4"}],"name":"InvalidSelector","type":"error"},{"inputs":[{"internalType":"uint256","name":"messageLen","type":"uint256"}],"name":"L2WithdrawalMessageWrongLength","type":"error"},{"inputs":[],"name":"LegacyBridgeNotSet","type":"error"},{"inputs":[],"name":"LegacyMethodForNonL1Token","type":"error"},{"inputs":[],"name":"NativeTokenVaultAlreadySet","type":"error"},{"inputs":[],"name":"NotInitializedReentrancyGuard","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[{"internalType":"enum SharedBridgeKey","name":"","type":"uint8"}],"name":"SharedBridgeValueNotSet","type":"error"},{"inputs":[],"name":"SlotOccupied","type":"error"},{"inputs":[],"name":"TokenNotLegacy","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnsupportedEncodingVersion","type":"error"},{"inputs":[],"name":"WithdrawalAlreadyFinalized","type":"error"},{"inputs":[{"internalType":"address","name":"providedL2Sender","type":"address"}],"name":"WrongL2Sender","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"WrongMsgLength","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"chainId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"txDataHash","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"l2DepositTxHash","type":"bytes32"}],"name":"BridgehubDepositFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"BRIDGE_HUB","outputs":[{"internalType":"contract IBridgehub","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"__DEPRECATED_admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"l1Token","type":"address"}],"name":"__DEPRECATED_chainBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"__DEPRECATED_l2BridgeAddress","outputs":[{"internalType":"address","name":"l2Bridge","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"__DEPRECATED_pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"address","name":"_depositSender","type":"address"},{"internalType":"bytes32","name":"_assetId","type":"bytes32"},{"internalType":"bytes","name":"_assetData","type":"bytes"},{"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[]"}],"name":"bridgeRecoverFailedTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"bytes32","name":"_txDataHash","type":"bytes32"},{"internalType":"bytes32","name":"_txHash","type":"bytes32"}],"name":"bridgehubConfirmL2TransactionForwarded","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"address","name":"_token","type":"address"}],"name":"chainBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"address","name":"_depositSender","type":"address"},{"internalType":"address","name":"_l1Token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"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[]"}],"name":"claimFailedDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_depositSender","type":"address"},{"internalType":"address","name":"_l1Token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"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[]"}],"name":"claimFailedDepositLegacyErc20Bridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bytes32","name":"l2DepositTxHash","type":"bytes32"}],"name":"depositHappened","outputs":[{"internalType":"bytes32","name":"depositDataHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes1","name":"_encodingVersion","type":"bytes1"},{"internalType":"address","name":"_originalCaller","type":"address"},{"internalType":"bytes32","name":"_assetId","type":"bytes32"},{"internalType":"bytes","name":"_transferData","type":"bytes"}],"name":"encodeTxDataHash","outputs":[{"internalType":"bytes32","name":"txDataHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint256","name":"l2BatchNumber","type":"uint256"},{"internalType":"uint256","name":"l2MessageIndex","type":"uint256"},{"internalType":"address","name":"l2Sender","type":"address"},{"internalType":"uint16","name":"l2TxNumberInBatch","type":"uint16"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"internalType":"struct FinalizeL1DepositParams","name":"_finalizeWithdrawalParams","type":"tuple"}],"name":"finalizeDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"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":"finalizeWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_eraPostDiamondUpgradeFirstBatch","type":"uint256"},{"internalType":"uint256","name":"_eraPostLegacyBridgeUpgradeFirstBatch","type":"uint256"},{"internalType":"uint256","name":"_eraLegacyBridgeLastDepositBatch","type":"uint256"},{"internalType":"uint256","name":"_eraLegacyBridgeLastDepositTxNumber","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint256","name":"l2BatchNumber","type":"uint256"},{"internalType":"uint256","name":"l2ToL1MessageNumber","type":"uint256"}],"name":"isWithdrawalFinalized","outputs":[{"internalType":"bool","name":"isFinalized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l1AssetRouter","outputs":[{"internalType":"contract IL1AssetRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l1NativeTokenVault","outputs":[{"internalType":"contract IL1NativeTokenVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"}],"name":"l2BridgeAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"legacyBridge","outputs":[{"internalType":"contract IL1ERC20Bridge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"address","name":"_token","type":"address"}],"name":"nullifyChainBalanceByNTV","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_l1AssetRouter","type":"address"}],"name":"setL1AssetRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IL1ERC20Bridge","name":"_legacyBridge","type":"address"}],"name":"setL1Erc20Bridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IL1NativeTokenVault","name":"_l1NativeTokenVault","type":"address"}],"name":"setL1NativeTokenVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"transferTokenToNTV","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60e060405234801561000f575f5ffd5b50604051613eab380380613eab83398101604081905261002e91610175565b61003661005b565b61003e6100a5565b6001600160a01b0392831660805260a0919091521660c0526101b5565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf48054600190915580156100a25760405163df3a8fdd60e01b815260040160405180910390fd5b50565b5f54610100900460ff16156101105760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b5f5460ff9081161461015f575f805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b03811681146100a2575f5ffd5b5f5f5f60608486031215610187575f5ffd5b835161019281610161565b6020850151604086015191945092506101aa81610161565b809150509250925092565b60805160a05160c051613c7f61022c5f395f611b6801525f8181610d7901528181610df40152818161217c015281816121e5015281816128140152818161287c015281816128b4015261291d01525f81816102d6015281816116310152818161254c015281816127240152612aa60152613c7f5ff3fe608060405234801561000f575f5ffd5b5060043610610201575f3560e01c8063780ce11411610123578063b7cc6f46116100b8578063f120e6c411610088578063f7a5cec01161006e578063f7a5cec01461051f578063f92ad2191461053f578063fdbb030114610552575f5ffd5b8063f120e6c4146104f9578063f2fde38b1461050c575f5ffd5b8063b7cc6f46146104a2578063c0991525146104b5578063c87325f1146104c8578063e30c3978146104db575f5ffd5b80638f31f052116100f35780638f31f0521461041f5780638fbb3711146104525780639cd45184146104655780639fa8826b14610478575f5ffd5b8063780ce114146103de57806379ba5097146103f15780638456cb59146103f95780638da5cb5b14610401575f5ffd5b80635de097b1116101995780636e9d7899116101695780636e9d7899146103835780636f513211146103a3578063715018a6146103c357806374beea82146103cb575f5ffd5b80635de097b1146102f85780636182877b1461030b5780636cdecb2b146103435780636d9860e114610363575f5ffd5b806340a434d5116101d457806340a434d5146102945780634bc2c8c0146102a75780635c975abb146102ba5780635d4edca7146102d1575f5ffd5b806307ee93551461020557806330bda03e146102645780633601e63e146102795780633f4ba83a1461028c575b5f5ffd5b61023a6102133660046130af565b5f90815260ce602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6102776102723660046130e7565b610587565b005b61027761028736600461329a565b610694565b61027761081b565b6102776102a23660046130e7565b61082d565b6102776102b5366004613359565b6109a2565b60975460ff165b604051901515815260200161025b565b61023a7f000000000000000000000000000000000000000000000000000000000000000081565b610277610306366004613382565b610a98565b610335610319366004613382565b60d260209081525f928352604080842090915290825290205481565b60405190815260200161025b565b60d45461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b60d55461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b60cd5461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b60d65461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b610277610b1d565b6102776103d9366004613426565b610b2e565b6102776103ec3660046130e7565b610b3a565b610277610c42565b610277610cf4565b60335473ffffffffffffffffffffffffffffffffffffffff1661023a565b6102c161042d366004613359565b60d060209081525f938452604080852082529284528284209052825290205460ff1681565b6102776104603660046134fd565b610d04565b610335610473366004613382565b610e5c565b610335610486366004613599565b60cf60209081525f928352604080842090915290825290205481565b6102776104b03660046130e7565b610e92565b6102776104c33660046135b9565b610f7e565b6102776104d636600461365d565b611122565b60655473ffffffffffffffffffffffffffffffffffffffff1661023a565b6103356105073660046136f8565b611246565b61027761051a3660046130e7565b6112b4565b60d35461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b61027761054d36600461378a565b611364565b61023a6105603660046130af565b60ce6020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b61058f611569565b60cd5473ffffffffffffffffffffffffffffffffffffffff16156106005760cd546040517f0dfb42bf00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811661064d576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60cd80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f8190036106f0576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001811461072a576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4556107615f8c8c8c8c8c8c8c8c8c8c6115ea565b60d5546040517f1346ca3b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690631346ca3b906107bd908e908e908e908e90600401613816565b5f604051808303815f87803b1580156107d4575f5ffd5b505af11580156107e6573d5f5f3e3d5ffd5b5050505060017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4555050505050505050505050565b610823611569565b61082b6117f4565b565b60d65473ffffffffffffffffffffffffffffffffffffffff163314610880576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b60d65473ffffffffffffffffffffffffffffffffffffffff9081169082166001036108ee57475f8080808085875af19050806108e8576040517f6d963f8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015261099e90829073ffffffffffffffffffffffffffffffffffffffff8516906370a0823190602401602060405180830381865afa15801561095c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109809190613850565b73ffffffffffffffffffffffffffffffffffffffff85169190611871565b5050565b60d55473ffffffffffffffffffffffffffffffffffffffff1633146109f5576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b6109fd611903565b5f83815260cf6020908152604080832084845290915290205415610a4d576040517fad2fa98e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f83815260cf60209081526040808320848452909152808220849055518291849186917fe4def01b981193a97a9e81230d7b9f31812ceaf23f864a828a82c687911cb2df91a4505050565b60d65473ffffffffffffffffffffffffffffffffffffffff163314610aeb576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b5f91825260d26020908152604080842073ffffffffffffffffffffffffffffffffffffffff9093168452919052812055565b610b25611569565b61082b5f611970565b610b37816119a1565b50565b610b42611569565b60d55473ffffffffffffffffffffffffffffffffffffffff1615610bae5760d5546040517f0dfb42bf00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016105f7565b73ffffffffffffffffffffffffffffffffffffffff8116610bfb576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b606554339073ffffffffffffffffffffffffffffffffffffffff168114610ceb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e6572000000000000000000000000000000000000000000000060648201526084016105f7565b610b3781611970565b610cfc611569565b61082b611db4565b60cd5473ffffffffffffffffffffffffffffffffffffffff163314610d57576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b5f610d63885f5f611e0f565b90505f610d70468b611e65565b9050610da660017f00000000000000000000000000000000000000000000000000000000000000008d84868d8d8d8d8d8d6115ea565b60d5546040517f1346ca3b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690631346ca3b90610e22907f0000000000000000000000000000000000000000000000000000000000000000908f9086908890600401613816565b5f604051808303815f87803b158015610e39575f5ffd5b505af1158015610e4b573d5f5f3e3d5ffd5b505050505050505050505050505050565b5f82815260d26020908152604080832073ffffffffffffffffffffffffffffffffffffffff851684529091529020545b92915050565b610e9a611569565b60d65473ffffffffffffffffffffffffffffffffffffffff1615610eea576040517f1c55230b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116610f37576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60d6546040517ffd3f60df00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a811660048301525f92169063fd3f60df90602401602060405180830381865afa158015610fec573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110109190613850565b90505f61101d468b611e65565b90508161102c57809150611065565b808214611065576040517f767eed0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6110718a5f5f611e0f565b90506110865f8e8e86858e8e8e8e8e8e6115ea565b60d55f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631346ca3b8e8e86856040518563ffffffff1660e01b81526004016110e69493929190613816565b5f604051808303815f87803b1580156110fd575f5ffd5b505af115801561110f573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f88815260ce602052604090205473ffffffffffffffffffffffffffffffffffffffff168061117d576040517f8efef97a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6040518060e001604052808b81526020018a81526020018981526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018861ffff16815260200187878080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060408051602087810282810182019093528782529283019290918891889182918501908490808284375f920191909152505050915250905061123a81610b2e565b50505050505050505050565b5f6112aa86868660d65f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1687878080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250611ec592505050565b9695505050505050565b6112bc611569565b6065805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811790915561131f60335473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b61136c612119565b5f54610100900460ff161580801561138a57505f54600160ff909116105b806113a35750303b1580156113a357505f5460ff166001145b61142f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016105f7565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561148b575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff86166114d8576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6114e186611970565b60c9545f036114ff5760c985905560ca84905560cb83905560cc8290555b8015611561575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b60335473ffffffffffffffffffffffffffffffffffffffff16331461082b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016105f7565b6115f2611903565b6040517fb292f5f10000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b292f5f190611674908e908b908b908b908b908b908b908b90600401613894565b602060405180830381865afa15801561168f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116b39190613924565b9050806116ec576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f5f6116fe8c888761ffff16612179565b1590508c158061170b5750805b91505080156117cc575f8b815260cf602090815260408083208a84529091528120549061173a8c8c8c85612232565b9050806117c95760d6545f9061178c907f0100000000000000000000000000000000000000000000000000000000000000908f908f9073ffffffffffffffffffffffffffffffffffffffff168f611ec5565b90508083146117c7576040517fc7c9660f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b50505b5050505f978852505060cf602090815260408088209388529290525084209390935550505050565b6117fc6122e3565b609780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526118fe90849061234f565b505050565b60975460ff161561082b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a207061757365640000000000000000000000000000000060448201526064016105f7565b606580547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055610b378161245c565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f8190036119fd576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018114611a37576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf455611a63611903565b81516020808401516040808601515f85815260d08552828120848252855282812082825290945292205490919060ff1615611aca576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f83815260d0602090815260408083208584528252808320848452909152812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580611b1d876124d2565b91509150611b2b8585612811565b15611c22576040517fbd7c541200000000000000000000000000000000000000000000000000000000815260048101859052602481018490525f907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063bd7c541290604401602060405180830381865afa158015611bc2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611be69190613924565b90508015611c20576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b611c2c85856128b1565b15611d005760cd546040517f4bed8212000000000000000000000000000000000000000000000000000000008152600481018690526024810185905273ffffffffffffffffffffffffffffffffffffffff90911690634bed821290604401602060405180830381865afa158015611ca5573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cc99190613924565b15611d00576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d5546040517f9c884fd100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690639c884fd190611d5a90889086908690600401613943565b5f604051808303815f87803b158015611d71575f5ffd5b505af1158015611d83573d5f5f3e3d5ffd5b50505050505050505060017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4555050565b611dbc611903565b609780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586118473390565b6060838383604051602001611e4d9392919092835273ffffffffffffffffffffffffffffffffffffffff918216602084015216604082015260600190565b60405160208183030381529060405290509392505050565b5f82611e7562010000600461398e565b60408051602081019390935273ffffffffffffffffffffffffffffffffffffffff918216908301528316606082015260800160405160208183030381529060405280519060200120905092915050565b5f7fff000000000000000000000000000000000000000000000000000000000000008616612046576040517f97bb3ce9000000000000000000000000000000000000000000000000000000008152600481018590525f9073ffffffffffffffffffffffffffffffffffffffff8516906397bb3ce990602401602060405180830381865afa158015611f58573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7c91906139ba565b90505f611f894683611e65565b9050808614611fe3576040517f1929b7de0000000000000000000000000000000000000000000000000000000081526004810187905273ffffffffffffffffffffffffffffffffffffffff831660248201526044016105f7565b5f611fed8561294f565b50506040805173ffffffffffffffffffffffffffffffffffffffff808c16602083015286169181019190915260608101829052909150608001604051602081830303815290604052805190602001209350505050612110565b7fff00000000000000000000000000000000000000000000000000000000000000868116016120de5785858584604051602001612085939291906139d5565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526120c19291602001613a20565b604051602081830303815290604052805190602001209050612110565b6040517f084a144900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b95945050505050565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4805460019091558015610b37576040517fdf3a8fdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f0000000000000000000000000000000000000000000000000000000000000000841480156121a9575060cb54155b156121e35760026040517f7774d2f90000000000000000000000000000000000000000000000000000000081526004016105f79190613a53565b7f00000000000000000000000000000000000000000000000000000000000000008414801561222a575060cb5483108061222a575060cc548210801561222a575060cb5483145b949350505050565b6040517ff120e6c40000000000000000000000000000000000000000000000000000000081525f90309063f120e6c490612276908490899089908990600401613a6d565b602060405180830381865afa9250505080156122cd575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526122ca91810190613850565b60015b6122d857505f61222a565b909114949350505050565b60975460ff1661082b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f742070617573656400000000000000000000000060448201526064016105f7565b5f6123b0826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166129ae9092919063ffffffff16565b905080515f14806123d05750808060200190518101906123d09190613924565b6118fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016105f7565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f60606124e6835f01518460a001516129bc565b60408051606080820183525f8083526020830152918101919091529193509150606084015184516040517fe52db4ca0000000000000000000000000000000000000000000000000000000081525f9173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169163e52db4ca916125839160040190815260200190565b602060405180830381865afa15801561259e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125c29190613850565b851490505f6125d562010000600361398e565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614806126455750612616618000600a61398e565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b80612677575086515f90815260ce602052604090205473ffffffffffffffffffffffffffffffffffffffff8481169116145b9050806126c8576040517f636c90db00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016105f7565b6040518060600160405280886080015161ffff168152602001836126ec57846126f9565b6126f9618000600a61398e565b73ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015181525093505050505f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166399c16d1a865f015187602001518860400151868a60c001516040518663ffffffff1660e01b8152600401612792959493929190613ac9565b602060405180830381865afa1580156127ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127d19190613924565b90508061280a576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050915091565b5f7f000000000000000000000000000000000000000000000000000000000000000083148015612841575060c954155b1561287a575f6040517f7774d2f90000000000000000000000000000000000000000000000000000000081526004016105f79190613a53565b7f0000000000000000000000000000000000000000000000000000000000000000831480156128aa575060c95482105b9392505050565b5f7f0000000000000000000000000000000000000000000000000000000000000000831480156128e1575060ca54155b1561291b5760016040517f7774d2f90000000000000000000000000000000000000000000000000000000081526004016105f79190613a53565b7f0000000000000000000000000000000000000000000000000000000000000000831480156128aa57505060ca5411919050565b5f5f5f835160601461298d576040517fde4c0b9600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838060200190518101906129a19190613b6c565b9196909550909350915050565b606061222a84845f85612e1c565b6004818101515f9160609183918291907fffffffff0000000000000000000000000000000000000000000000000000000060e083901b167f93f69f070000000000000000000000000000000000000000000000000000000001612b6657603887511015612a5a5786516040517f97e1359e0000000000000000000000000000000000000000000000000000000081526004016105f791815260200190565b60148188018101519082016020898201810151929550909250506040517fe52db4ca000000000000000000000000000000000000000000000000000000008152600481018a90529094507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e52db4ca90602401602060405180830381865afa158015612b00573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b249190613850565b9550612b5f5f848187815b6040519080825280601f01601f191660200182016040528015612b59576020820181803683370190505b50612f31565b9450612e11565b7fee5d333f0000000000000000000000000000000000000000000000000000000060e083901b7fffffffff000000000000000000000000000000000000000000000000000000001601612d0b578651604c14612bf35786516040517f97e1359e0000000000000000000000000000000000000000000000000000000081526004016105f791815260200190565b868101601481015160288083015160489093015160d654604080517f19a2a28500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80881660048301529151939a50949850929095019491909116916319a2a28591602480830192602092919082900301815f875af1158015612c89573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cad9190613850565b96505f612cba4683611e65565b9050808814612cf5576040517f1850b46b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612d025f86848983612b2f565b96505050612e11565b7f6377b02f0000000000000000000000000000000000000000000000000000000060e083901b7fffffffff000000000000000000000000000000000000000000000000000000001601612db757604487511015612da25786516040517f61cdb17e0000000000000000000000000000000000000000000000000000000081526105f791604491600401918252602082015260400190565b6040878201810151965001612b5f8782612f66565b6040517f12ba286f0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1660048201526024016105f7565b505050509250929050565b606082471015612eae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016105f7565b5f5f8673ffffffffffffffffffffffffffffffffffffffff168587604051612ed69190613bad565b5f6040518083038185875af1925050503d805f8114612f10576040519150601f19603f3d011682016040523d82523d5f602084013e612f15565b606091505b5091509150612f2687838387612fd1565b979650505050505050565b60608585858585604051602001612f4c959493929190613bb8565b604051602081830303815290604052905095945050505050565b60605f828451612f769190613c24565b90508067ffffffffffffffff811115612f9157612f91613112565b6040519080825280601f01601f191660200182016040528015612fbb576020820181803683370190505b50915080836020018501602084015e5092915050565b606083156130665782515f0361305f5773ffffffffffffffffffffffffffffffffffffffff85163b61305f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105f7565b508161222a565b61222a838381511561307b5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f79190613c37565b5f602082840312156130bf575f5ffd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610b37575f5ffd5b5f602082840312156130f7575f5ffd5b81356128aa816130c6565b803561310d816130c6565b919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60405160e0810167ffffffffffffffff8111828210171561316257613162613112565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156131af576131af613112565b604052919050565b5f82601f8301126131c6575f5ffd5b813567ffffffffffffffff8111156131e0576131e0613112565b61321160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613168565b818152846020838601011115613225575f5ffd5b816020850160208301375f918101602001919091529392505050565b803561ffff8116811461310d575f5ffd5b5f5f83601f840112613262575f5ffd5b50813567ffffffffffffffff811115613279575f5ffd5b6020830191508360208260051b8501011115613293575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f5f6101208b8d0312156132b4575f5ffd5b8a35995060208b01356132c6816130c6565b985060408b0135975060608b013567ffffffffffffffff8111156132e8575f5ffd5b6132f48d828e016131b7565b97505060808b0135955060a08b0135945060c08b0135935061331860e08c01613241565b92506101008b013567ffffffffffffffff811115613334575f5ffd5b6133408d828e01613252565b915080935050809150509295989b9194979a5092959850565b5f5f5f6060848603121561336b575f5ffd5b505081359360208301359350604090920135919050565b5f5f60408385031215613393575f5ffd5b8235915060208301356133a5816130c6565b809150509250929050565b5f82601f8301126133bf575f5ffd5b813567ffffffffffffffff8111156133d9576133d9613112565b8060051b6133e960208201613168565b91825260208185018101929081019086841115613404575f5ffd5b6020860192505b838310156112aa57823582526020928301929091019061340b565b5f60208284031215613436575f5ffd5b813567ffffffffffffffff81111561344c575f5ffd5b820160e0818503121561345d575f5ffd5b61346561313f565b81358152602080830135908201526040808301359082015261348960608301613102565b606082015261349a60808301613241565b608082015260a082013567ffffffffffffffff8111156134b8575f5ffd5b6134c4868285016131b7565b60a08301525060c082013567ffffffffffffffff8111156134e3575f5ffd5b6134ef868285016133b0565b60c083015250949350505050565b5f5f5f5f5f5f5f5f5f6101008a8c031215613516575f5ffd5b8935613521816130c6565b985060208a0135613531816130c6565b975060408a0135965060608a0135955060808a0135945060a08a0135935061355b60c08b01613241565b925060e08a013567ffffffffffffffff811115613576575f5ffd5b6135828c828d01613252565b915080935050809150509295985092959850929598565b5f5f604083850312156135aa575f5ffd5b50508035926020909101359150565b5f5f5f5f5f5f5f5f5f5f6101208b8d0312156135d3575f5ffd5b8a35995060208b01356135e5816130c6565b985060408b01356135f5816130c6565b975060608b0135965060808b0135955060a08b0135945060c08b0135935061331860e08c01613241565b5f5f83601f84011261362f575f5ffd5b50813567ffffffffffffffff811115613646575f5ffd5b602083019150836020828501011115613293575f5ffd5b5f5f5f5f5f5f5f5f60c0898b031215613674575f5ffd5b88359750602089013596506040890135955061369260608a01613241565b9450608089013567ffffffffffffffff8111156136ad575f5ffd5b6136b98b828c0161361f565b90955093505060a089013567ffffffffffffffff8111156136d8575f5ffd5b6136e48b828c01613252565b999c989b5096995094979396929594505050565b5f5f5f5f5f6080868803121561370c575f5ffd5b85357fff000000000000000000000000000000000000000000000000000000000000008116811461373b575f5ffd5b9450602086013561374b816130c6565b935060408601359250606086013567ffffffffffffffff81111561376d575f5ffd5b6137798882890161361f565b969995985093965092949392505050565b5f5f5f5f5f60a0868803121561379e575f5ffd5b85356137a9816130c6565b97602087013597506040870135966060810135965060800135945092505050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b84815273ffffffffffffffffffffffffffffffffffffffff84166020820152826040820152608060608201525f6112aa60808301846137ca565b5f60208284031215613860575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b88815287602082015286604082015285606082015261ffff8516608082015260e060a08201528260e08201525f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8411156138ed575f5ffd5b8360051b808661010085013782016101000190506002831061391157613911613867565b8260c08301529998505050505050505050565b5f60208284031215613934575f5ffd5b815180151581146128aa575f5ffd5b838152826020820152606060408201525f61211060608301846137ca565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b73ffffffffffffffffffffffffffffffffffffffff8181168382160190811115610e8c57610e8c613961565b5f602082840312156139ca575f5ffd5b81516128aa816130c6565b73ffffffffffffffffffffffffffffffffffffffff84168152826020820152606060408201525f61211060608301846137ca565b5f81518060208401855e5f93019283525090919050565b7fff00000000000000000000000000000000000000000000000000000000000000831681525f61222a6001830184613a09565b6020810160048310613a6757613a67613867565b91905290565b7fff000000000000000000000000000000000000000000000000000000000000008516815273ffffffffffffffffffffffffffffffffffffffff84166020820152826040820152608060608201525f6112aa60808301846137ca565b85815284602082015283604082015260a0606082015261ffff83511660a082015273ffffffffffffffffffffffffffffffffffffffff60208401511660c08201525f6040840151606060e0840152613b256101008401826137ca565b8381036080850152845180825260208087019350909101905f5b81811015613b5d578351835260209384019390920191600101613b3f565b50909998505050505050505050565b5f5f5f60608486031215613b7e575f5ffd5b83516020850151909350613b91816130c6565b6040850151909250613ba2816130c6565b809150509250925092565b5f6128aa8284613a09565b73ffffffffffffffffffffffffffffffffffffffff8616815273ffffffffffffffffffffffffffffffffffffffff8516602082015273ffffffffffffffffffffffffffffffffffffffff8416604082015282606082015260a060808201525f612f2660a08301846137ca565b81810381811115610e8c57610e8c613961565b602081525f6128aa60208301846137ca56fea264697066735822122035c6512a9f4efc2d3012aaa5f1666319cb2310ca32f5e5387d85ea20c1341f2364736f6c634300081c0033000000000000000000000000236d1c3ff32bd0ca26b72af287e895627c0478ce000000000000000000000000000000000000000000000000000000000000010e0000000000000000000000006d6e010a2680e2e5a3b097ce411528b36d880ef6
Deployed Bytecode
0x608060405234801561000f575f5ffd5b5060043610610201575f3560e01c8063780ce11411610123578063b7cc6f46116100b8578063f120e6c411610088578063f7a5cec01161006e578063f7a5cec01461051f578063f92ad2191461053f578063fdbb030114610552575f5ffd5b8063f120e6c4146104f9578063f2fde38b1461050c575f5ffd5b8063b7cc6f46146104a2578063c0991525146104b5578063c87325f1146104c8578063e30c3978146104db575f5ffd5b80638f31f052116100f35780638f31f0521461041f5780638fbb3711146104525780639cd45184146104655780639fa8826b14610478575f5ffd5b8063780ce114146103de57806379ba5097146103f15780638456cb59146103f95780638da5cb5b14610401575f5ffd5b80635de097b1116101995780636e9d7899116101695780636e9d7899146103835780636f513211146103a3578063715018a6146103c357806374beea82146103cb575f5ffd5b80635de097b1146102f85780636182877b1461030b5780636cdecb2b146103435780636d9860e114610363575f5ffd5b806340a434d5116101d457806340a434d5146102945780634bc2c8c0146102a75780635c975abb146102ba5780635d4edca7146102d1575f5ffd5b806307ee93551461020557806330bda03e146102645780633601e63e146102795780633f4ba83a1461028c575b5f5ffd5b61023a6102133660046130af565b5f90815260ce602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6102776102723660046130e7565b610587565b005b61027761028736600461329a565b610694565b61027761081b565b6102776102a23660046130e7565b61082d565b6102776102b5366004613359565b6109a2565b60975460ff165b604051901515815260200161025b565b61023a7f000000000000000000000000236d1c3ff32bd0ca26b72af287e895627c0478ce81565b610277610306366004613382565b610a98565b610335610319366004613382565b60d260209081525f928352604080842090915290825290205481565b60405190815260200161025b565b60d45461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b60d55461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b60cd5461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b60d65461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b610277610b1d565b6102776103d9366004613426565b610b2e565b6102776103ec3660046130e7565b610b3a565b610277610c42565b610277610cf4565b60335473ffffffffffffffffffffffffffffffffffffffff1661023a565b6102c161042d366004613359565b60d060209081525f938452604080852082529284528284209052825290205460ff1681565b6102776104603660046134fd565b610d04565b610335610473366004613382565b610e5c565b610335610486366004613599565b60cf60209081525f928352604080842090915290825290205481565b6102776104b03660046130e7565b610e92565b6102776104c33660046135b9565b610f7e565b6102776104d636600461365d565b611122565b60655473ffffffffffffffffffffffffffffffffffffffff1661023a565b6103356105073660046136f8565b611246565b61027761051a3660046130e7565b6112b4565b60d35461023a9073ffffffffffffffffffffffffffffffffffffffff1681565b61027761054d36600461378a565b611364565b61023a6105603660046130af565b60ce6020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b61058f611569565b60cd5473ffffffffffffffffffffffffffffffffffffffff16156106005760cd546040517f0dfb42bf00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811661064d576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60cd80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f8190036106f0576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001811461072a576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4556107615f8c8c8c8c8c8c8c8c8c8c6115ea565b60d5546040517f1346ca3b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690631346ca3b906107bd908e908e908e908e90600401613816565b5f604051808303815f87803b1580156107d4575f5ffd5b505af11580156107e6573d5f5f3e3d5ffd5b5050505060017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4555050505050505050505050565b610823611569565b61082b6117f4565b565b60d65473ffffffffffffffffffffffffffffffffffffffff163314610880576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b60d65473ffffffffffffffffffffffffffffffffffffffff9081169082166001036108ee57475f8080808085875af19050806108e8576040517f6d963f8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015261099e90829073ffffffffffffffffffffffffffffffffffffffff8516906370a0823190602401602060405180830381865afa15801561095c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109809190613850565b73ffffffffffffffffffffffffffffffffffffffff85169190611871565b5050565b60d55473ffffffffffffffffffffffffffffffffffffffff1633146109f5576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b6109fd611903565b5f83815260cf6020908152604080832084845290915290205415610a4d576040517fad2fa98e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f83815260cf60209081526040808320848452909152808220849055518291849186917fe4def01b981193a97a9e81230d7b9f31812ceaf23f864a828a82c687911cb2df91a4505050565b60d65473ffffffffffffffffffffffffffffffffffffffff163314610aeb576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b5f91825260d26020908152604080842073ffffffffffffffffffffffffffffffffffffffff9093168452919052812055565b610b25611569565b61082b5f611970565b610b37816119a1565b50565b610b42611569565b60d55473ffffffffffffffffffffffffffffffffffffffff1615610bae5760d5546040517f0dfb42bf00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016105f7565b73ffffffffffffffffffffffffffffffffffffffff8116610bfb576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b606554339073ffffffffffffffffffffffffffffffffffffffff168114610ceb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e6572000000000000000000000000000000000000000000000060648201526084016105f7565b610b3781611970565b610cfc611569565b61082b611db4565b60cd5473ffffffffffffffffffffffffffffffffffffffff163314610d57576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b5f610d63885f5f611e0f565b90505f610d70468b611e65565b9050610da660017f000000000000000000000000000000000000000000000000000000000000010e8d84868d8d8d8d8d8d6115ea565b60d5546040517f1346ca3b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690631346ca3b90610e22907f000000000000000000000000000000000000000000000000000000000000010e908f9086908890600401613816565b5f604051808303815f87803b158015610e39575f5ffd5b505af1158015610e4b573d5f5f3e3d5ffd5b505050505050505050505050505050565b5f82815260d26020908152604080832073ffffffffffffffffffffffffffffffffffffffff851684529091529020545b92915050565b610e9a611569565b60d65473ffffffffffffffffffffffffffffffffffffffff1615610eea576040517f1c55230b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116610f37576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60d6546040517ffd3f60df00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a811660048301525f92169063fd3f60df90602401602060405180830381865afa158015610fec573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110109190613850565b90505f61101d468b611e65565b90508161102c57809150611065565b808214611065576040517f767eed0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6110718a5f5f611e0f565b90506110865f8e8e86858e8e8e8e8e8e6115ea565b60d55f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631346ca3b8e8e86856040518563ffffffff1660e01b81526004016110e69493929190613816565b5f604051808303815f87803b1580156110fd575f5ffd5b505af115801561110f573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f88815260ce602052604090205473ffffffffffffffffffffffffffffffffffffffff168061117d576040517f8efef97a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6040518060e001604052808b81526020018a81526020018981526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018861ffff16815260200187878080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060408051602087810282810182019093528782529283019290918891889182918501908490808284375f920191909152505050915250905061123a81610b2e565b50505050505050505050565b5f6112aa86868660d65f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1687878080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250611ec592505050565b9695505050505050565b6112bc611569565b6065805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811790915561131f60335473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b61136c612119565b5f54610100900460ff161580801561138a57505f54600160ff909116105b806113a35750303b1580156113a357505f5460ff166001145b61142f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016105f7565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561148b575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff86166114d8576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6114e186611970565b60c9545f036114ff5760c985905560ca84905560cb83905560cc8290555b8015611561575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b60335473ffffffffffffffffffffffffffffffffffffffff16331461082b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016105f7565b6115f2611903565b6040517fb292f5f10000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000236d1c3ff32bd0ca26b72af287e895627c0478ce169063b292f5f190611674908e908b908b908b908b908b908b908b90600401613894565b602060405180830381865afa15801561168f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116b39190613924565b9050806116ec576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f5f6116fe8c888761ffff16612179565b1590508c158061170b5750805b91505080156117cc575f8b815260cf602090815260408083208a84529091528120549061173a8c8c8c85612232565b9050806117c95760d6545f9061178c907f0100000000000000000000000000000000000000000000000000000000000000908f908f9073ffffffffffffffffffffffffffffffffffffffff168f611ec5565b90508083146117c7576040517fc7c9660f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b50505b5050505f978852505060cf602090815260408088209388529290525084209390935550505050565b6117fc6122e3565b609780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526118fe90849061234f565b505050565b60975460ff161561082b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a207061757365640000000000000000000000000000000060448201526064016105f7565b606580547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055610b378161245c565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4545f8190036119fd576040517fdd7e362100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018114611a37576040517fab143c0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60027f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf455611a63611903565b81516020808401516040808601515f85815260d08552828120848252855282812082825290945292205490919060ff1615611aca576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f83815260d0602090815260408083208584528252808320848452909152812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580611b1d876124d2565b91509150611b2b8585612811565b15611c22576040517fbd7c541200000000000000000000000000000000000000000000000000000000815260048101859052602481018490525f907f0000000000000000000000006d6e010a2680e2e5a3b097ce411528b36d880ef673ffffffffffffffffffffffffffffffffffffffff169063bd7c541290604401602060405180830381865afa158015611bc2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611be69190613924565b90508015611c20576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b611c2c85856128b1565b15611d005760cd546040517f4bed8212000000000000000000000000000000000000000000000000000000008152600481018690526024810185905273ffffffffffffffffffffffffffffffffffffffff90911690634bed821290604401602060405180830381865afa158015611ca5573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cc99190613924565b15611d00576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d5546040517f9c884fd100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690639c884fd190611d5a90889086908690600401613943565b5f604051808303815f87803b158015611d71575f5ffd5b505af1158015611d83573d5f5f3e3d5ffd5b50505050505050505060017f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4555050565b611dbc611903565b609780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586118473390565b6060838383604051602001611e4d9392919092835273ffffffffffffffffffffffffffffffffffffffff918216602084015216604082015260600190565b60405160208183030381529060405290509392505050565b5f82611e7562010000600461398e565b60408051602081019390935273ffffffffffffffffffffffffffffffffffffffff918216908301528316606082015260800160405160208183030381529060405280519060200120905092915050565b5f7fff000000000000000000000000000000000000000000000000000000000000008616612046576040517f97bb3ce9000000000000000000000000000000000000000000000000000000008152600481018590525f9073ffffffffffffffffffffffffffffffffffffffff8516906397bb3ce990602401602060405180830381865afa158015611f58573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7c91906139ba565b90505f611f894683611e65565b9050808614611fe3576040517f1929b7de0000000000000000000000000000000000000000000000000000000081526004810187905273ffffffffffffffffffffffffffffffffffffffff831660248201526044016105f7565b5f611fed8561294f565b50506040805173ffffffffffffffffffffffffffffffffffffffff808c16602083015286169181019190915260608101829052909150608001604051602081830303815290604052805190602001209350505050612110565b7fff00000000000000000000000000000000000000000000000000000000000000868116016120de5785858584604051602001612085939291906139d5565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526120c19291602001613a20565b604051602081830303815290604052805190602001209050612110565b6040517f084a144900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b95945050505050565b7f8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4805460019091558015610b37576040517fdf3a8fdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000010e841480156121a9575060cb54155b156121e35760026040517f7774d2f90000000000000000000000000000000000000000000000000000000081526004016105f79190613a53565b7f000000000000000000000000000000000000000000000000000000000000010e8414801561222a575060cb5483108061222a575060cc548210801561222a575060cb5483145b949350505050565b6040517ff120e6c40000000000000000000000000000000000000000000000000000000081525f90309063f120e6c490612276908490899089908990600401613a6d565b602060405180830381865afa9250505080156122cd575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526122ca91810190613850565b60015b6122d857505f61222a565b909114949350505050565b60975460ff1661082b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f742070617573656400000000000000000000000060448201526064016105f7565b5f6123b0826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166129ae9092919063ffffffff16565b905080515f14806123d05750808060200190518101906123d09190613924565b6118fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016105f7565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f60606124e6835f01518460a001516129bc565b60408051606080820183525f8083526020830152918101919091529193509150606084015184516040517fe52db4ca0000000000000000000000000000000000000000000000000000000081525f9173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000236d1c3ff32bd0ca26b72af287e895627c0478ce169163e52db4ca916125839160040190815260200190565b602060405180830381865afa15801561259e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125c29190613850565b851490505f6125d562010000600361398e565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614806126455750612616618000600a61398e565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b80612677575086515f90815260ce602052604090205473ffffffffffffffffffffffffffffffffffffffff8481169116145b9050806126c8576040517f636c90db00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016105f7565b6040518060600160405280886080015161ffff168152602001836126ec57846126f9565b6126f9618000600a61398e565b73ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015181525093505050505f7f000000000000000000000000236d1c3ff32bd0ca26b72af287e895627c0478ce73ffffffffffffffffffffffffffffffffffffffff166399c16d1a865f015187602001518860400151868a60c001516040518663ffffffff1660e01b8152600401612792959493929190613ac9565b602060405180830381865afa1580156127ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127d19190613924565b90508061280a576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050915091565b5f7f000000000000000000000000000000000000000000000000000000000000010e83148015612841575060c954155b1561287a575f6040517f7774d2f90000000000000000000000000000000000000000000000000000000081526004016105f79190613a53565b7f000000000000000000000000000000000000000000000000000000000000010e831480156128aa575060c95482105b9392505050565b5f7f000000000000000000000000000000000000000000000000000000000000010e831480156128e1575060ca54155b1561291b5760016040517f7774d2f90000000000000000000000000000000000000000000000000000000081526004016105f79190613a53565b7f000000000000000000000000000000000000000000000000000000000000010e831480156128aa57505060ca5411919050565b5f5f5f835160601461298d576040517fde4c0b9600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838060200190518101906129a19190613b6c565b9196909550909350915050565b606061222a84845f85612e1c565b6004818101515f9160609183918291907fffffffff0000000000000000000000000000000000000000000000000000000060e083901b167f93f69f070000000000000000000000000000000000000000000000000000000001612b6657603887511015612a5a5786516040517f97e1359e0000000000000000000000000000000000000000000000000000000081526004016105f791815260200190565b60148188018101519082016020898201810151929550909250506040517fe52db4ca000000000000000000000000000000000000000000000000000000008152600481018a90529094507f000000000000000000000000236d1c3ff32bd0ca26b72af287e895627c0478ce73ffffffffffffffffffffffffffffffffffffffff169063e52db4ca90602401602060405180830381865afa158015612b00573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b249190613850565b9550612b5f5f848187815b6040519080825280601f01601f191660200182016040528015612b59576020820181803683370190505b50612f31565b9450612e11565b7fee5d333f0000000000000000000000000000000000000000000000000000000060e083901b7fffffffff000000000000000000000000000000000000000000000000000000001601612d0b578651604c14612bf35786516040517f97e1359e0000000000000000000000000000000000000000000000000000000081526004016105f791815260200190565b868101601481015160288083015160489093015160d654604080517f19a2a28500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80881660048301529151939a50949850929095019491909116916319a2a28591602480830192602092919082900301815f875af1158015612c89573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cad9190613850565b96505f612cba4683611e65565b9050808814612cf5576040517f1850b46b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612d025f86848983612b2f565b96505050612e11565b7f6377b02f0000000000000000000000000000000000000000000000000000000060e083901b7fffffffff000000000000000000000000000000000000000000000000000000001601612db757604487511015612da25786516040517f61cdb17e0000000000000000000000000000000000000000000000000000000081526105f791604491600401918252602082015260400190565b6040878201810151965001612b5f8782612f66565b6040517f12ba286f0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1660048201526024016105f7565b505050509250929050565b606082471015612eae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016105f7565b5f5f8673ffffffffffffffffffffffffffffffffffffffff168587604051612ed69190613bad565b5f6040518083038185875af1925050503d805f8114612f10576040519150601f19603f3d011682016040523d82523d5f602084013e612f15565b606091505b5091509150612f2687838387612fd1565b979650505050505050565b60608585858585604051602001612f4c959493929190613bb8565b604051602081830303815290604052905095945050505050565b60605f828451612f769190613c24565b90508067ffffffffffffffff811115612f9157612f91613112565b6040519080825280601f01601f191660200182016040528015612fbb576020820181803683370190505b50915080836020018501602084015e5092915050565b606083156130665782515f0361305f5773ffffffffffffffffffffffffffffffffffffffff85163b61305f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105f7565b508161222a565b61222a838381511561307b5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f79190613c37565b5f602082840312156130bf575f5ffd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610b37575f5ffd5b5f602082840312156130f7575f5ffd5b81356128aa816130c6565b803561310d816130c6565b919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60405160e0810167ffffffffffffffff8111828210171561316257613162613112565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156131af576131af613112565b604052919050565b5f82601f8301126131c6575f5ffd5b813567ffffffffffffffff8111156131e0576131e0613112565b61321160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613168565b818152846020838601011115613225575f5ffd5b816020850160208301375f918101602001919091529392505050565b803561ffff8116811461310d575f5ffd5b5f5f83601f840112613262575f5ffd5b50813567ffffffffffffffff811115613279575f5ffd5b6020830191508360208260051b8501011115613293575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f5f6101208b8d0312156132b4575f5ffd5b8a35995060208b01356132c6816130c6565b985060408b0135975060608b013567ffffffffffffffff8111156132e8575f5ffd5b6132f48d828e016131b7565b97505060808b0135955060a08b0135945060c08b0135935061331860e08c01613241565b92506101008b013567ffffffffffffffff811115613334575f5ffd5b6133408d828e01613252565b915080935050809150509295989b9194979a5092959850565b5f5f5f6060848603121561336b575f5ffd5b505081359360208301359350604090920135919050565b5f5f60408385031215613393575f5ffd5b8235915060208301356133a5816130c6565b809150509250929050565b5f82601f8301126133bf575f5ffd5b813567ffffffffffffffff8111156133d9576133d9613112565b8060051b6133e960208201613168565b91825260208185018101929081019086841115613404575f5ffd5b6020860192505b838310156112aa57823582526020928301929091019061340b565b5f60208284031215613436575f5ffd5b813567ffffffffffffffff81111561344c575f5ffd5b820160e0818503121561345d575f5ffd5b61346561313f565b81358152602080830135908201526040808301359082015261348960608301613102565b606082015261349a60808301613241565b608082015260a082013567ffffffffffffffff8111156134b8575f5ffd5b6134c4868285016131b7565b60a08301525060c082013567ffffffffffffffff8111156134e3575f5ffd5b6134ef868285016133b0565b60c083015250949350505050565b5f5f5f5f5f5f5f5f5f6101008a8c031215613516575f5ffd5b8935613521816130c6565b985060208a0135613531816130c6565b975060408a0135965060608a0135955060808a0135945060a08a0135935061355b60c08b01613241565b925060e08a013567ffffffffffffffff811115613576575f5ffd5b6135828c828d01613252565b915080935050809150509295985092959850929598565b5f5f604083850312156135aa575f5ffd5b50508035926020909101359150565b5f5f5f5f5f5f5f5f5f5f6101208b8d0312156135d3575f5ffd5b8a35995060208b01356135e5816130c6565b985060408b01356135f5816130c6565b975060608b0135965060808b0135955060a08b0135945060c08b0135935061331860e08c01613241565b5f5f83601f84011261362f575f5ffd5b50813567ffffffffffffffff811115613646575f5ffd5b602083019150836020828501011115613293575f5ffd5b5f5f5f5f5f5f5f5f60c0898b031215613674575f5ffd5b88359750602089013596506040890135955061369260608a01613241565b9450608089013567ffffffffffffffff8111156136ad575f5ffd5b6136b98b828c0161361f565b90955093505060a089013567ffffffffffffffff8111156136d8575f5ffd5b6136e48b828c01613252565b999c989b5096995094979396929594505050565b5f5f5f5f5f6080868803121561370c575f5ffd5b85357fff000000000000000000000000000000000000000000000000000000000000008116811461373b575f5ffd5b9450602086013561374b816130c6565b935060408601359250606086013567ffffffffffffffff81111561376d575f5ffd5b6137798882890161361f565b969995985093965092949392505050565b5f5f5f5f5f60a0868803121561379e575f5ffd5b85356137a9816130c6565b97602087013597506040870135966060810135965060800135945092505050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b84815273ffffffffffffffffffffffffffffffffffffffff84166020820152826040820152608060608201525f6112aa60808301846137ca565b5f60208284031215613860575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b88815287602082015286604082015285606082015261ffff8516608082015260e060a08201528260e08201525f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8411156138ed575f5ffd5b8360051b808661010085013782016101000190506002831061391157613911613867565b8260c08301529998505050505050505050565b5f60208284031215613934575f5ffd5b815180151581146128aa575f5ffd5b838152826020820152606060408201525f61211060608301846137ca565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b73ffffffffffffffffffffffffffffffffffffffff8181168382160190811115610e8c57610e8c613961565b5f602082840312156139ca575f5ffd5b81516128aa816130c6565b73ffffffffffffffffffffffffffffffffffffffff84168152826020820152606060408201525f61211060608301846137ca565b5f81518060208401855e5f93019283525090919050565b7fff00000000000000000000000000000000000000000000000000000000000000831681525f61222a6001830184613a09565b6020810160048310613a6757613a67613867565b91905290565b7fff000000000000000000000000000000000000000000000000000000000000008516815273ffffffffffffffffffffffffffffffffffffffff84166020820152826040820152608060608201525f6112aa60808301846137ca565b85815284602082015283604082015260a0606082015261ffff83511660a082015273ffffffffffffffffffffffffffffffffffffffff60208401511660c08201525f6040840151606060e0840152613b256101008401826137ca565b8381036080850152845180825260208087019350909101905f5b81811015613b5d578351835260209384019390920191600101613b3f565b50909998505050505050505050565b5f5f5f60608486031215613b7e575f5ffd5b83516020850151909350613b91816130c6565b6040850151909250613ba2816130c6565b809150509250925092565b5f6128aa8284613a09565b73ffffffffffffffffffffffffffffffffffffffff8616815273ffffffffffffffffffffffffffffffffffffffff8516602082015273ffffffffffffffffffffffffffffffffffffffff8416604082015282606082015260a060808201525f612f2660a08301846137ca565b81810381811115610e8c57610e8c613961565b602081525f6128aa60208301846137ca56fea264697066735822122035c6512a9f4efc2d3012aaa5f1666319cb2310ca32f5e5387d85ea20c1341f2364736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000236d1c3ff32bd0ca26b72af287e895627c0478ce000000000000000000000000000000000000000000000000000000000000010e0000000000000000000000006d6e010a2680e2e5a3b097ce411528b36d880ef6
-----Decoded View---------------
Arg [0] : _bridgehub (address): 0x236D1c3Ff32Bd0Ca26b72Af287E895627c0478cE
Arg [1] : _eraChainId (uint256): 270
Arg [2] : _eraDiamondProxy (address): 0x6d6e010A2680E2E5a3b097ce411528b36d880EF6
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000236d1c3ff32bd0ca26b72af287e895627c0478ce
Arg [1] : 000000000000000000000000000000000000000000000000000000000000010e
Arg [2] : 0000000000000000000000006d6e010a2680e2e5a3b097ce411528b36d880ef6
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.