Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x61010060 | 19312660 | 261 days ago | IN | 0 ETH | 0.30889127 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
PolygonValidiumStorageMigration
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 999999 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.20; import "./PolygonRollupBaseEtrogNoGap.sol"; import "../../../interfaces/IDataAvailabilityProtocol.sol"; import "../../../interfaces/IPolygonValidium.sol"; /** * Contract responsible for managing the states and the updates of L2 network. * There will be a trusted sequencer, which is able to send transactions. * Any user can force some transaction and the sequencer will have a timeout to add them in the queue. * The sequenced state is deterministic and can be precalculated before it's actually verified by a zkProof. * The aggregators will be able to verify the sequenced state with zkProofs and therefore make available the withdrawals from L2 network. * To enter and exit of the L2 network will be used a PolygonZkEVMBridge smart contract that will be deployed in both networks. * It is advised to use timelocks for the admin address in case of Validium since if can change the dataAvailabilityProtocol */ contract PolygonValidiumStorageMigration is PolygonRollupBaseEtrogNoGap, IPolygonValidium { using SafeERC20Upgradeable for IERC20Upgradeable; /** * @notice Struct which will be used to call sequenceBatches * @param transactionsHash keccak256 hash of the L2 ethereum transactions EIP-155 or pre-EIP-155 with signature: * EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data, chainid, 0, 0,) || v || r || s * pre-EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data) || v || r || s * @param forcedGlobalExitRoot Global exit root, empty when sequencing a non forced batch * @param forcedTimestamp Minimum timestamp of the force batch data, empty when sequencing a non forced batch * @param forcedBlockHashL1 blockHash snapshot of the force batch data, empty when sequencing a non forced batch */ struct ValidiumBatchData { bytes32 transactionsHash; bytes32 forcedGlobalExitRoot; uint64 forcedTimestamp; bytes32 forcedBlockHashL1; } // Copy and clean the previous storage values to make it storage compatible with the new contracts // Data Availability Protocol Address /// @custom:oz-renamed-from dataAvailabilityProtocol IDataAvailabilityProtocol internal _dataAvailabilityProtocol; // Indicates if sequence with data avialability is allowed // This allow the sequencer to post the data and skip the Data comittee /// @custom:oz-renamed-from isSequenceWithDataAvailabilityAllowed bool internal _isSequenceWithDataAvailabilityAllowed; /** * @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. */ uint256[49] private _gap; // Data Availability Protocol Address IDataAvailabilityProtocol public dataAvailabilityProtocol; // Indicates if sequence with data avialability is allowed // This allow the sequencer to post the data and skip the Data comittee bool public isSequenceWithDataAvailabilityAllowed; /** * @dev Emitted when the admin updates the data availability protocol */ event SetDataAvailabilityProtocol(address newDataAvailabilityProtocol); /** * @dev Emitted when switch the ability to sequence with data availability */ event SwitchSequenceWithDataAvailability(); /** * @param _globalExitRootManager Global exit root manager address * @param _pol POL token address * @param _bridgeAddress Bridge address * @param _rollupManager Global exit root manager address */ constructor( IPolygonZkEVMGlobalExitRootV2 _globalExitRootManager, IERC20Upgradeable _pol, IPolygonZkEVMBridgeV2 _bridgeAddress, PolygonRollupManager _rollupManager ) PolygonRollupBaseEtrogNoGap( _globalExitRootManager, _pol, _bridgeAddress, _rollupManager ) {} // Reinitialize the contract, the call will be done the same transaction the contract is upgraded function initializeMigration() external virtual onlyRollupManager reinitializer(2) { // Copy the previous storage slots dataAvailabilityProtocol = _dataAvailabilityProtocol; isSequenceWithDataAvailabilityAllowed = _isSequenceWithDataAvailabilityAllowed; // Clean the previous storage slots _dataAvailabilityProtocol = IDataAvailabilityProtocol(address(0)); _isSequenceWithDataAvailabilityAllowed = false; } ///////////////////////////////////// // Sequence/Verify batches functions //////////////////////////////////// /** * @notice Allows a sequencer to send multiple batches * @param batches Struct array which holds the necessary data to append new batches to the sequence * @param maxSequenceTimestamp Max timestamp of the sequence. This timestamp must be inside a safety range (actual + 36 seconds). * This timestamp should be equal or higher of the last block inside the sequence, otherwise this batch will be invalidated by circuit. * @param initSequencedBatch This parameter must match the current last batch sequenced. * This will be a protection for the sequencer to avoid sending undesired data * @param l2Coinbase Address that will receive the fees from L2 * @param dataAvailabilityMessage Byte array containing the signatures and all the addresses of the committee in ascending order * [signature 0, ..., signature requiredAmountOfSignatures -1, address 0, ... address N] * note that each ECDSA signatures are used, therefore each one must be 65 bytes * note Pol is not a reentrant token */ function sequenceBatchesValidium( ValidiumBatchData[] calldata batches, uint64 maxSequenceTimestamp, uint64 initSequencedBatch, address l2Coinbase, bytes calldata dataAvailabilityMessage ) external onlyTrustedSequencer { uint256 batchesNum = batches.length; if (batchesNum == 0) { revert SequenceZeroBatches(); } if (batchesNum > _MAX_VERIFY_BATCHES) { revert ExceedMaxVerifyBatches(); } // Check max sequence timestamp inside of range if ( uint256(maxSequenceTimestamp) > (block.timestamp + TIMESTAMP_RANGE) ) { revert MaxTimestampSequenceInvalid(); } // Update global exit root if there are new deposits bridgeAddress.updateGlobalExitRoot(); // Get global batch variables bytes32 l1InfoRoot = globalExitRootManager.getRoot(); // Store storage variables in memory, to save gas, because will be overrided multiple times uint64 currentLastForceBatchSequenced = lastForceBatchSequenced; bytes32 currentAccInputHash = lastAccInputHash; // Store in a temporal variable, for avoid access again the storage slot uint64 initLastForceBatchSequenced = currentLastForceBatchSequenced; // Accumulated sequenced transaction hash to verify them afterward against the dataAvailabilityProtocol bytes32 accumulatedNonForcedTransactionsHash = bytes32(0); for (uint256 i = 0; i < batchesNum; i++) { // Load current sequence ValidiumBatchData memory currentBatch = batches[i]; // Check if it's a forced batch if (currentBatch.forcedTimestamp > 0) { currentLastForceBatchSequenced++; // Check forced data matches bytes32 hashedForcedBatchData = keccak256( abi.encodePacked( currentBatch.transactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, currentBatch.forcedBlockHashL1 ) ); if ( hashedForcedBatchData != forcedBatches[currentLastForceBatchSequenced] ) { revert ForcedDataDoesNotMatch(); } // Calculate next accumulated input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentBatch.transactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, l2Coinbase, currentBatch.forcedBlockHashL1 ) ); // Delete forceBatch data since won't be used anymore delete forcedBatches[currentLastForceBatchSequenced]; } else { // Accumulate non forced transactions hash accumulatedNonForcedTransactionsHash = keccak256( abi.encodePacked( accumulatedNonForcedTransactionsHash, currentBatch.transactionsHash ) ); // Note that forcedGlobalExitRoot and forcedBlockHashL1 remain unused and unchecked in this path // The synchronizer should be aware of that // Calculate next accumulated input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentBatch.transactionsHash, l1InfoRoot, maxSequenceTimestamp, l2Coinbase, bytes32(0) ) ); } } // Sanity check, should be unreachable if (currentLastForceBatchSequenced > lastForceBatch) { revert ForceBatchesOverflow(); } // Store back the storage variables lastAccInputHash = currentAccInputHash; uint256 nonForcedBatchesSequenced = batchesNum; // Check if there has been forced batches if (currentLastForceBatchSequenced != initLastForceBatchSequenced) { uint64 forcedBatchesSequenced = currentLastForceBatchSequenced - initLastForceBatchSequenced; // substract forced batches nonForcedBatchesSequenced -= forcedBatchesSequenced; // Transfer pol for every forced batch submitted pol.safeTransfer( address(rollupManager), calculatePolPerForceBatch() * (forcedBatchesSequenced) ); // Store new last force batch sequenced lastForceBatchSequenced = currentLastForceBatchSequenced; } // Pay collateral for every non-forced batch submitted if (nonForcedBatchesSequenced != 0) { pol.safeTransferFrom( msg.sender, address(rollupManager), rollupManager.getBatchFee() * nonForcedBatchesSequenced ); // Validate that the data availability protocol accepts the dataAvailabilityMessage // note This is a view function, so there's not much risk even if this contract was vulnerable to reentrant attacks dataAvailabilityProtocol.verifyMessage( accumulatedNonForcedTransactionsHash, dataAvailabilityMessage ); } uint64 currentBatchSequenced = rollupManager.onSequenceBatches( uint64(batchesNum), currentAccInputHash ); // Check init sequenced batch if ( initSequencedBatch != (currentBatchSequenced - uint64(batchesNum)) ) { revert InitSequencedBatchDoesNotMatch(); } emit SequenceBatches(currentBatchSequenced, l1InfoRoot); } /** * @notice Allows a sequencer to send multiple batches * @param batches Struct array which holds the necessary data to append new batches to the sequence * @param maxSequenceTimestamp Max timestamp of the sequence. This timestamp must be inside a safety range (actual + 36 seconds). * This timestamp should be equal or higher of the last block inside the sequence, otherwise this batch will be invalidated by circuit. * @param initSequencedBatch This parameter must match the current last batch sequenced. * This will be a protection for the sequencer to avoid sending undesired data * @param l2Coinbase Address that will receive the fees from L2 * note Pol is not a reentrant token */ function sequenceBatches( BatchData[] calldata batches, uint64 maxSequenceTimestamp, uint64 initSequencedBatch, address l2Coinbase ) public override { if (!isSequenceWithDataAvailabilityAllowed) { revert SequenceWithDataAvailabilityNotAllowed(); } super.sequenceBatches( batches, maxSequenceTimestamp, initSequencedBatch, l2Coinbase ); } ////////////////// // admin functions ////////////////// /** * @notice Allow the admin to set a new data availability protocol * @param newDataAvailabilityProtocol Address of the new data availability protocol */ function setDataAvailabilityProtocol( IDataAvailabilityProtocol newDataAvailabilityProtocol ) external onlyAdmin { dataAvailabilityProtocol = newDataAvailabilityProtocol; emit SetDataAvailabilityProtocol(address(newDataAvailabilityProtocol)); } /** * @notice Allow the admin to switch the sequence with data availability * @param newIsSequenceWithDataAvailabilityAllowed Boolean to switch */ function switchSequenceWithDataAvailability( bool newIsSequenceWithDataAvailabilityAllowed ) external onlyAdmin { if ( newIsSequenceWithDataAvailabilityAllowed == isSequenceWithDataAvailabilityAllowed ) { revert SwitchToSameValue(); } isSequenceWithDataAvailabilityAllowed = newIsSequenceWithDataAvailabilityAllowed; emit SwitchSequenceWithDataAvailability(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControlUpgradeable { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.1) (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] * ``` * 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 v4.4.1 (token/ERC20/extensions/draft-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. */ interface IERC20PermitUpgradeable { /** * @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]. */ 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 v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20MetadataUpgradeable is IERC20Upgradeable { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @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.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; import "../extensions/draft-IERC20PermitUpgradeable.sol"; import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable { using AddressUpgradeable for address; function safeTransfer( IERC20Upgradeable token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20Upgradeable 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( IERC20Upgradeable 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)); } function safeIncreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20PermitUpgradeable 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(IERC20Upgradeable 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"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.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 * ==== * * [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://diligence.consensys.net/posts/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.5.11/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 Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../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; } /** * @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 v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165Upgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable { function __ERC165_init() internal onlyInitializing { } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165Upgradeable).interfaceId; } /** * @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 v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165Upgradeable { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library MathUpgradeable { 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) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 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 10, 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 * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/MathUpgradeable.sol"; /** * @dev String operations. */ library StringsUpgradeable { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = MathUpgradeable.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, MathUpgradeable.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.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. * * The initial owner is set to the address provided by the deployer. 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 Ownable is Context { address private _owner; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @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 { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @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 { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _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); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol) pragma solidity ^0.8.20; /** * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC. */ interface IERC1967 { /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Emitted when the beacon is changed. */ event BeaconUpgraded(address indexed beacon); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.20; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {UpgradeableBeacon} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol) pragma solidity ^0.8.20; import {Proxy} from "../Proxy.sol"; import {ERC1967Utils} from "./ERC1967Utils.sol"; /** * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an * implementation address that can be changed. This address is stored in storage in the location specified by * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the * implementation behind the proxy. */ contract ERC1967Proxy is Proxy { /** * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`. * * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor. * * Requirements: * * - If `data` is empty, `msg.value` must be zero. */ constructor(address implementation, bytes memory _data) payable { ERC1967Utils.upgradeToAndCall(implementation, _data); } /** * @dev Returns the current implementation address. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` */ function _implementation() internal view virtual override returns (address) { return ERC1967Utils.getImplementation(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol) pragma solidity ^0.8.20; import {IBeacon} from "../beacon/IBeacon.sol"; import {Address} from "../../utils/Address.sol"; import {StorageSlot} from "../../utils/StorageSlot.sol"; /** * @dev This abstract contract provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. */ library ERC1967Utils { // We re-declare ERC-1967 events here because they can't be used directly from IERC1967. // This will be fixed in Solidity 0.8.21. At that point we should remove these events. /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Emitted when the beacon is changed. */ event BeaconUpgraded(address indexed beacon); /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev The `implementation` of the proxy is invalid. */ error ERC1967InvalidImplementation(address implementation); /** * @dev The `admin` of the proxy is invalid. */ error ERC1967InvalidAdmin(address admin); /** * @dev The `beacon` of the proxy is invalid. */ error ERC1967InvalidBeacon(address beacon); /** * @dev An upgrade function sees `msg.value > 0` that may be lost. */ error ERC1967NonPayable(); /** * @dev Returns the current implementation address. */ function getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the EIP1967 implementation slot. */ function _setImplementation(address newImplementation) private { if (newImplementation.code.length == 0) { revert ERC1967InvalidImplementation(newImplementation); } StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Performs implementation upgrade with additional setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-Upgraded} event. */ function upgradeToAndCall(address newImplementation, bytes memory data) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); if (data.length > 0) { Address.functionDelegateCall(newImplementation, data); } else { _checkNonPayable(); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Returns the current admin. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(ADMIN_SLOT).value; } /** * @dev Stores a new address in the EIP1967 admin slot. */ function _setAdmin(address newAdmin) private { if (newAdmin == address(0)) { revert ERC1967InvalidAdmin(address(0)); } StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {IERC1967-AdminChanged} event. */ function changeAdmin(address newAdmin) internal { emit AdminChanged(getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Returns the current beacon. */ function getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(BEACON_SLOT).value; } /** * @dev Stores a new beacon in the EIP1967 beacon slot. */ function _setBeacon(address newBeacon) private { if (newBeacon.code.length == 0) { revert ERC1967InvalidBeacon(newBeacon); } StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon; address beaconImplementation = IBeacon(newBeacon).implementation(); if (beaconImplementation.code.length == 0) { revert ERC1967InvalidImplementation(beaconImplementation); } } /** * @dev Change the beacon and trigger a setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-BeaconUpgraded} event. * * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for * efficiency. */ function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal { _setBeacon(newBeacon); emit BeaconUpgraded(newBeacon); if (data.length > 0) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } else { _checkNonPayable(); } } /** * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract * if an upgrade doesn't perform an initialization call. */ function _checkNonPayable() private { if (msg.value > 0) { revert ERC1967NonPayable(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol) pragma solidity ^0.8.20; /** * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to * be specified by overriding the virtual {_implementation} function. * * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a * different contract through the {_delegate} function. * * The success and return data of the delegated call will be returned back to the caller of the proxy. */ abstract contract Proxy { /** * @dev Delegates the current call to `implementation`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev This is a virtual function that should be overridden so it returns the address to which the fallback * function and {_fallback} should delegate. */ function _implementation() internal view virtual returns (address); /** * @dev Delegates the current call to the address returned by `_implementation()`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _fallback() internal virtual { _delegate(_implementation()); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other * function in the contract matches the call data. */ fallback() external payable virtual { _fallback(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/ProxyAdmin.sol) pragma solidity ^0.8.20; import {ITransparentUpgradeableProxy} from "./TransparentUpgradeableProxy.sol"; import {Ownable} from "../../access/Ownable.sol"; /** * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}. */ contract ProxyAdmin is Ownable { /** * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address)` * and `upgradeAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called, * while `upgradeAndCall` will invoke the `receive` function if the second argument is the empty byte string. * If the getter returns `"5.0.0"`, only `upgradeAndCall(address,bytes)` is present, and the second argument must * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function * during an upgrade. */ string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; /** * @dev Sets the initial owner who can perform upgrades. */ constructor(address initialOwner) Ownable(initialOwner) {} /** * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. * See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}. * * Requirements: * * - This contract must be the admin of `proxy`. * - If `data` is empty, `msg.value` must be zero. */ function upgradeAndCall( ITransparentUpgradeableProxy proxy, address implementation, bytes memory data ) public payable virtual onlyOwner { proxy.upgradeToAndCall{value: msg.value}(implementation, data); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/TransparentUpgradeableProxy.sol) pragma solidity ^0.8.20; import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol"; import {ERC1967Proxy} from "../ERC1967/ERC1967Proxy.sol"; import {IERC1967} from "../../interfaces/IERC1967.sol"; import {ProxyAdmin} from "./ProxyAdmin.sol"; /** * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy} * does not implement this interface directly, and its upgradeability mechanism is implemented by an internal dispatch * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not * include them in the ABI so this interface must be used to interact with it. */ interface ITransparentUpgradeableProxy is IERC1967 { function upgradeToAndCall(address, bytes calldata) external payable; } /** * @dev This contract implements a proxy that is upgradeable through an associated {ProxyAdmin} instance. * * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector * clashing], which can potentially be used in an attack, this contract uses the * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two * things that go hand in hand: * * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if * that call matches the {ITransparentUpgradeableProxy-upgradeToAndCall} function exposed by the proxy itself. * 2. If the admin calls the proxy, it can call the `upgradeToAndCall` function but any other call won't be forwarded to * the implementation. If the admin tries to call a function on the implementation it will fail with an error indicating * the proxy admin cannot fallback to the target implementation. * * These properties mean that the admin account can only be used for upgrading the proxy, so it's best if it's a * dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to * call a function from the proxy implementation. For this reason, the proxy deploys an instance of {ProxyAdmin} and * allows upgrades only if they come through it. You should think of the `ProxyAdmin` instance as the administrative * interface of the proxy, including the ability to change who can trigger upgrades by transferring ownership. * * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not * inherit from that interface, and instead `upgradeToAndCall` is implicitly implemented using a custom dispatch * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the * implementation. * * NOTE: This proxy does not inherit from {Context} deliberately. The {ProxyAdmin} of this contract won't send a * meta-transaction in any way, and any other meta-transaction setup should be made in the implementation contract. * * IMPORTANT: This contract avoids unnecessary storage reads by setting the admin only during construction as an * immutable variable, preventing any changes thereafter. However, the admin slot defined in ERC-1967 can still be * overwritten by the implementation logic pointed to by this proxy. In such cases, the contract may end up in an * undesirable state where the admin slot is different from the actual admin. * * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the * compiler will not check that there are no selector conflicts, due to the note above. A selector clash between any new * function and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This * could render the `upgradeToAndCall` function inaccessible, preventing upgradeability and compromising transparency. */ contract TransparentUpgradeableProxy is ERC1967Proxy { // An immutable address for the admin to avoid unnecessary SLOADs before each call // at the expense of removing the ability to change the admin once it's set. // This is acceptable if the admin is always a ProxyAdmin instance or similar contract // with its own ability to transfer the permissions to another account. address private immutable _admin; /** * @dev The proxy caller is the current admin, and can't fallback to the proxy target. */ error ProxyDeniedAdminAccess(); /** * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`, * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in * {ERC1967Proxy-constructor}. */ constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) { _admin = address(new ProxyAdmin(initialOwner)); // Set the storage value and emit an event for ERC-1967 compatibility ERC1967Utils.changeAdmin(_proxyAdmin()); } /** * @dev Returns the admin of this proxy. */ function _proxyAdmin() internal virtual returns (address) { return _admin; } /** * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior. */ function _fallback() internal virtual override { if (msg.sender == _proxyAdmin()) { if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) { revert ProxyDeniedAdminAccess(); } else { _dispatchUpgradeToAndCall(); } } else { super._fallback(); } } /** * @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}. * * Requirements: * * - If `data` is empty, `msg.value` must be zero. */ function _dispatchUpgradeToAndCall() private { (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes)); ERC1967Utils.upgradeToAndCall(newImplementation, data); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @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 or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * 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. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @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`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) 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 FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol) pragma solidity ^0.8.20; /** * @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 Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.20; /** * @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(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` */ 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: AGPL-3.0 pragma solidity ^0.8.20; interface IBasePolygonZkEVMGlobalExitRoot { /** * @dev Thrown when the caller is not the allowed contracts */ error OnlyAllowedContracts(); function updateExitRoot(bytes32 newRollupExitRoot) external; function globalExitRootMap( bytes32 globalExitRootNum ) external returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; interface IPolygonZkEVMBridge { /** * @dev Thrown when sender is not the PolygonZkEVM address */ error OnlyPolygonZkEVM(); /** * @dev Thrown when the destination network is invalid */ error DestinationNetworkInvalid(); /** * @dev Thrown when the amount does not match msg.value */ error AmountDoesNotMatchMsgValue(); /** * @dev Thrown when user is bridging tokens and is also sending a value */ error MsgValueNotZero(); /** * @dev Thrown when the Ether transfer on claimAsset fails */ error EtherTransferFailed(); /** * @dev Thrown when the message transaction on claimMessage fails */ error MessageFailed(); /** * @dev Thrown when the global exit root does not exist */ error GlobalExitRootInvalid(); /** * @dev Thrown when the smt proof does not match */ error InvalidSmtProof(); /** * @dev Thrown when an index is already claimed */ error AlreadyClaimed(); /** * @dev Thrown when the owner of permit does not match the sender */ error NotValidOwner(); /** * @dev Thrown when the spender of the permit does not match this contract address */ error NotValidSpender(); /** * @dev Thrown when the amount of the permit does not match */ error NotValidAmount(); /** * @dev Thrown when the permit data contains an invalid signature */ error NotValidSignature(); function bridgeAsset( uint32 destinationNetwork, address destinationAddress, uint256 amount, address token, bool forceUpdateGlobalExitRoot, bytes calldata permitData ) external payable; function bridgeMessage( uint32 destinationNetwork, address destinationAddress, bool forceUpdateGlobalExitRoot, bytes calldata metadata ) external payable; function claimAsset( bytes32[32] calldata smtProof, uint32 index, bytes32 mainnetExitRoot, bytes32 rollupExitRoot, uint32 originNetwork, address originTokenAddress, uint32 destinationNetwork, address destinationAddress, uint256 amount, bytes calldata metadata ) external; function claimMessage( bytes32[32] calldata smtProof, uint32 index, bytes32 mainnetExitRoot, bytes32 rollupExitRoot, uint32 originNetwork, address originAddress, uint32 destinationNetwork, address destinationAddress, uint256 amount, bytes calldata metadata ) external; function updateGlobalExitRoot() external; function activateEmergencyState() external; function deactivateEmergencyState() external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; interface IPolygonZkEVMErrors { /** * @dev Thrown when the pending state timeout exceeds the _HALT_AGGREGATION_TIMEOUT */ error PendingStateTimeoutExceedHaltAggregationTimeout(); /** * @dev Thrown when the trusted aggregator timeout exceeds the _HALT_AGGREGATION_TIMEOUT */ error TrustedAggregatorTimeoutExceedHaltAggregationTimeout(); /** * @dev Thrown when the caller is not the admin */ error OnlyAdmin(); /** * @dev Thrown when the caller is not the trusted sequencer */ error OnlyTrustedSequencer(); /** * @dev Thrown when the caller is not the trusted aggregator */ error OnlyTrustedAggregator(); /** * @dev Thrown when attempting to sequence 0 batches */ error SequenceZeroBatches(); /** * @dev Thrown when attempting to sequence or verify more batches than _MAX_VERIFY_BATCHES */ error ExceedMaxVerifyBatches(); /** * @dev Thrown when the forced data does not match */ error ForcedDataDoesNotMatch(); /** * @dev Thrown when the sequenced timestamp is below the forced minimum timestamp */ error SequencedTimestampBelowForcedTimestamp(); /** * @dev Thrown when a global exit root is not zero and does not exist */ error GlobalExitRootNotExist(); /** * @dev Thrown when transactions array length is above _MAX_TRANSACTIONS_BYTE_LENGTH. */ error TransactionsLengthAboveMax(); /** * @dev Thrown when a sequenced timestamp is not inside a correct range. */ error SequencedTimestampInvalid(); /** * @dev Thrown when there are more sequenced force batches than were actually submitted, should be unreachable */ error ForceBatchesOverflow(); /** * @dev Thrown when there are more sequenced force batches than were actually submitted */ error TrustedAggregatorTimeoutNotExpired(); /** * @dev Thrown when attempting to access a pending state that does not exist */ error PendingStateDoesNotExist(); /** * @dev Thrown when the init num batch does not match with the one in the pending state */ error InitNumBatchDoesNotMatchPendingState(); /** * @dev Thrown when the old state root of a certain batch does not exist */ error OldStateRootDoesNotExist(); /** * @dev Thrown when the init verification batch is above the last verification batch */ error InitNumBatchAboveLastVerifiedBatch(); /** * @dev Thrown when the final verification batch is below or equal the last verification batch */ error FinalNumBatchBelowLastVerifiedBatch(); /** * @dev Thrown when the zkproof is not valid */ error InvalidProof(); /** * @dev Thrown when attempting to consolidate a pending state not yet consolidable */ error PendingStateNotConsolidable(); /** * @dev Thrown when attempting to consolidate a pending state that is already consolidated or does not exist */ error PendingStateInvalid(); /** * @dev Thrown when the matic amount is below the necessary matic fee */ error NotEnoughMaticAmount(); /** * @dev Thrown when attempting to sequence a force batch using sequenceForceBatches and the * force timeout did not expire */ error ForceBatchTimeoutNotExpired(); /** * @dev Thrown when attempting to set a new trusted aggregator timeout equal or bigger than current one */ error NewTrustedAggregatorTimeoutMustBeLower(); /** * @dev Thrown when attempting to set a new pending state timeout equal or bigger than current one */ error NewPendingStateTimeoutMustBeLower(); /** * @dev Thrown when attempting to set a new multiplier batch fee in a invalid range of values */ error InvalidRangeMultiplierBatchFee(); /** * @dev Thrown when attempting to set a batch time target in an invalid range of values */ error InvalidRangeBatchTimeTarget(); /** * @dev Thrown when attempting to set a force batch timeout in an invalid range of values */ error InvalidRangeForceBatchTimeout(); /** * @dev Thrown when the caller is not the pending admin */ error OnlyPendingAdmin(); /** * @dev Thrown when the final pending state num is not in a valid range */ error FinalPendingStateNumInvalid(); /** * @dev Thrown when the final num batch does not match with the one in the pending state */ error FinalNumBatchDoesNotMatchPendingState(); /** * @dev Thrown when the stored root matches the new root proving a different state */ error StoredRootMustBeDifferentThanNewRoot(); /** * @dev Thrown when the batch is already verified when attempting to activate the emergency state */ error BatchAlreadyVerified(); /** * @dev Thrown when the batch is not sequenced or not at the end of a sequence when attempting to activate the emergency state */ error BatchNotSequencedOrNotSequenceEnd(); /** * @dev Thrown when the halt timeout is not expired when attempting to activate the emergency state */ error HaltTimeoutNotExpired(); /** * @dev Thrown when the old accumulate input hash does not exist */ error OldAccInputHashDoesNotExist(); /** * @dev Thrown when the new accumulate input hash does not exist */ error NewAccInputHashDoesNotExist(); /** * @dev Thrown when the new state root is not inside prime */ error NewStateRootNotInsidePrime(); /** * @dev Thrown when force batch is not allowed */ error ForceBatchNotAllowed(); /** * @dev Thrown when try to activate force batches when they are already active */ error ForceBatchesAlreadyActive(); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; /** * @dev Define interface verifier */ interface IVerifierRollup { function verifyProof( bytes32[24] calldata proof, uint256[1] calldata pubSignals ) external view returns (bool); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; /** * @dev Contract helper responsible to manage the emergency state */ contract EmergencyManager { /** * @dev Thrown when emergency state is active, and the function requires otherwise */ error OnlyNotEmergencyState(); /** * @dev Thrown when emergency state is not active, and the function requires otherwise */ error OnlyEmergencyState(); /** * @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. */ uint256[10] private _gap; // Indicates whether the emergency state is active or not bool public isEmergencyState; /** * @dev Emitted when emergency state is activated */ event EmergencyStateActivated(); /** * @dev Emitted when emergency state is deactivated */ event EmergencyStateDeactivated(); /** * @notice Only allows a function to be callable if emergency state is unactive */ modifier ifNotEmergencyState() { if (isEmergencyState) { revert OnlyNotEmergencyState(); } _; } /** * @notice Only allows a function to be callable if emergency state is active */ modifier ifEmergencyState() { if (!isEmergencyState) { revert OnlyEmergencyState(); } _; } /** * @notice Activate emergency state */ function _activateEmergencyState() internal virtual ifNotEmergencyState { isEmergencyState = true; emit EmergencyStateActivated(); } /** * @notice Deactivate emergency state */ function _deactivateEmergencyState() internal virtual ifEmergencyState { isEmergencyState = false; emit EmergencyStateDeactivated(); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "../../../interfaces/IPolygonZkEVMGlobalExitRootV2.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../../../../interfaces/IPolygonZkEVMErrors.sol"; import "../../../interfaces/IPolygonZkEVMVEtrogErrors.sol"; import "../../../PolygonRollupManager.sol"; import "../../../interfaces/IPolygonRollupBase.sol"; import "../../../interfaces/IPolygonZkEVMBridgeV2.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; import "../../../lib/PolygonConstantsBase.sol"; /** * Contract responsible for managing the states and the updates of L2 network. * There will be a trusted sequencer, which is able to send transactions. * Any user can force some transaction and the sequencer will have a timeout to add them in the queue. * The sequenced state is deterministic and can be precalculated before it's actually verified by a zkProof. * The aggregators will be able to verify the sequenced state with zkProofs and therefore make available the withdrawals from L2 network. * To enter and exit of the L2 network will be used a PolygonZkEVMBridge smart contract that will be deployed in both networks. */ abstract contract PolygonRollupBaseEtrogNoGap is Initializable, PolygonConstantsBase, IPolygonZkEVMVEtrogErrors, IPolygonRollupBase { using SafeERC20Upgradeable for IERC20Upgradeable; /** * @notice Struct which will be used to call sequenceBatches * @param transactions L2 ethereum transactions EIP-155 or pre-EIP-155 with signature: * EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data, chainid, 0, 0,) || v || r || s * pre-EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data) || v || r || s * @param forcedGlobalExitRoot Global exit root, empty when sequencing a non forced batch * @param forcedTimestamp Minimum timestamp of the force batch data, empty when sequencing a non forced batch * @param forcedBlockHashL1 blockHash snapshot of the force batch data, empty when sequencing a non forced batch */ struct BatchData { bytes transactions; bytes32 forcedGlobalExitRoot; uint64 forcedTimestamp; bytes32 forcedBlockHashL1; } // Max transactions bytes that can be added in a single batch // Max keccaks circuit = (2**23 / 155286) * 44 = 2376 // Bytes per keccak = 136 // Minimum Static keccaks batch = 2 // Max bytes allowed = (2376 - 2) * 136 = 322864 bytes - 1 byte padding // Rounded to 300000 bytes // In order to process the transaction, the data is approximately hashed twice for ecrecover: // 300000 bytes / 2 = 150000 bytes // Since geth pool currently only accepts at maximum 128kb transactions: // https://github.com/ethereum/go-ethereum/blob/master/core/txpool/txpool.go#L54 // We will limit this length to be compliant with the geth restrictions since our node will use it // We let 8kb as a sanity margin uint256 internal constant _MAX_TRANSACTIONS_BYTE_LENGTH = 120000; // Max force batch transaction length // This is used to avoid huge calldata attacks, where the attacker call force batches from another contract uint256 internal constant _MAX_FORCE_BATCH_BYTE_LENGTH = 5000; // In order to encode the initialize transaction of the bridge there's have a constant part and the metadata which is variable // Note the total transaction will be constrained to 65535 to avoid attacks and simplify the implementation // List rlp: 1 listLenLen "0xf9" (0xf7 + 2), + listLen 2 (32 bytes + txData bytes) (do not accept more than 65535 bytes) // First byte of the initialize bridge tx, indicates a list with a lengt of 2 bytes // Since the minimum constant bytes will be: 259 (tx data empty) + 31 (tx parameters) = 259 (0x103) will always take 2 bytes to express the lenght of the rlp // Note that more than 2 bytes of list len is not supported, since it's constrained to 65535 uint8 public constant INITIALIZE_TX_BRIDGE_LIST_LEN_LEN = 0xf9; // Tx parameters until the bridge address bytes public constant INITIALIZE_TX_BRIDGE_PARAMS = hex"80808401c9c38094"; // RLP encoded metadata (non empty) // TxData bytes: 164 bytes data ( signature 4 bytes + 5 parameters*32bytes + // (abi encoded metadata: 32 bytes position + 32 bytes len + 32 bytes position name + 32 bytes length name + 32 bytes position Symbol + 32 bytes length Symbol //+ 32 bytes decimal )) min 7*32 bytes = // = 164 bytes + 224 bytes = 388 (0x0184) minimum // Extra data: nameLen padded to 32 bytes + symbol len padded to 32 bytes // Constant bytes: 1 nonce "0x80" + 1 gasPrice "0x80" + 5 gasLimit "0x8401c9c380" (30M gas) // + 21 to ("0x94" + bridgeAddress") + 1 value "0x80" + 1 stringLenLen "0xb9" (0xb7 + 2) + // stringLen (0x0184 + nameLen padded to 32 bytes + symbol len padded to 32 bytes) + txData bytes = 32 bytes + txData bytes uint16 public constant INITIALIZE_TX_CONSTANT_BYTES = 32; // Tx parameters after the bridge address bytes public constant INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS = hex"80b9"; // RLP empty metadata // TxData empty metadata bytes: 164 bytes data ( signature 4 bytes + 5 parameters*32bytes + // (abi encoded metadata: 32 bytes position + 32 bytes len = 2*32 bytes = // = 164 bytes + 64 bytes = 228 (0xe4) // Constant bytes empty metadata : 1 nonce "0x80" + 1 gasPrice "0x80" + 5 gasLimit "0x8401c9c380" (30M gas) // + 21 to ("0x94" + bridgeAddress") + 1 value "0x80" + 1 stringLenLen "0xb8" (0xb7 + 1) + // 1 stringLen (0xe4) + txData bytes = 31 bytes + txData bytes empty metadata 228 = 259 uint16 public constant INITIALIZE_TX_CONSTANT_BYTES_EMPTY_METADATA = 31; uint8 public constant INITIALIZE_TX_DATA_LEN_EMPTY_METADATA = 228; // 0xe4 // Tx parameters after the bridge address bytes public constant INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS_EMPTY_METADATA = hex"80b8"; // Signature used to initialize the bridge // V parameter of the initialize signature uint8 public constant SIGNATURE_INITIALIZE_TX_V = 27; // R parameter of the initialize signature bytes32 public constant SIGNATURE_INITIALIZE_TX_R = 0x00000000000000000000000000000000000000000000000000000005ca1ab1e0; // S parameter of the initialize signature bytes32 public constant SIGNATURE_INITIALIZE_TX_S = 0x000000000000000000000000000000000000000000000000000000005ca1ab1e; // Effective percentage of the initalize transaction bytes1 public constant INITIALIZE_TX_EFFECTIVE_PERCENTAGE = 0xFF; // Global Exit Root address L2 IBasePolygonZkEVMGlobalExitRoot public constant GLOBAL_EXIT_ROOT_MANAGER_L2 = IBasePolygonZkEVMGlobalExitRoot( 0xa40D5f56745a118D0906a34E69aeC8C0Db1cB8fA ); // Timestamp range that's given to the sequencer as a safety measure to avoid reverts if the transaction is mined to quickly uint256 public constant TIMESTAMP_RANGE = 36; // POL token address IERC20Upgradeable public immutable pol; // Global Exit Root interface IPolygonZkEVMGlobalExitRootV2 public immutable globalExitRootManager; // PolygonZkEVM Bridge Address IPolygonZkEVMBridgeV2 public immutable bridgeAddress; // Rollup manager PolygonRollupManager public immutable rollupManager; // Address that will be able to adjust contract parameters address public admin; // This account will be able to accept the admin role address public pendingAdmin; // Trusted sequencer address address public trustedSequencer; // Trusted sequencer URL string public trustedSequencerURL; // L2 network name string public networkName; // Current accumulate input hash bytes32 public lastAccInputHash; // Queue of forced batches with their associated data // ForceBatchNum --> hashedForcedBatchData // hashedForcedBatchData: hash containing the necessary information to force a batch: // keccak256(keccak256(bytes transactions), bytes32 forcedGlobalExitRoot, unint64 forcedTimestamp, bytes32 forcedBlockHashL1) mapping(uint64 => bytes32) public forcedBatches; // Last forced batch uint64 public lastForceBatch; // Last forced batch included in the sequence uint64 public lastForceBatchSequenced; // Force batch timeout uint64 public forceBatchTimeout; // Indicates what address is able to do forced batches // If the address is set to 0, forced batches are open to everyone address public forceBatchAddress; // Token address that will be used to pay gas fees in this rollup. This variable it's just for read purposes address public gasTokenAddress; // Native network of the token address of the gas tokena address. This variable it's just for read purposes uint32 public gasTokenNetwork; /** * @dev Emitted when the trusted sequencer sends a new batch of transactions */ event SequenceBatches(uint64 indexed numBatch, bytes32 l1InfoRoot); /** * @dev Emitted when a batch is forced */ event ForceBatch( uint64 indexed forceBatchNum, bytes32 lastGlobalExitRoot, address sequencer, bytes transactions ); /** * @dev Emitted when forced batches are sequenced by not the trusted sequencer */ event SequenceForceBatches(uint64 indexed numBatch); /** * @dev Emitted when the contract is initialized, contain the first sequenced transaction */ event InitialSequenceBatches( bytes transactions, bytes32 lastGlobalExitRoot, address sequencer ); /** * @dev Emitted when a aggregator verifies batches */ event VerifyBatches( uint64 indexed numBatch, bytes32 stateRoot, address indexed aggregator ); /** * @dev Emitted when the admin updates the trusted sequencer address */ event SetTrustedSequencer(address newTrustedSequencer); /** * @dev Emitted when the admin updates the sequencer URL */ event SetTrustedSequencerURL(string newTrustedSequencerURL); /** * @dev Emitted when the admin update the force batch timeout */ event SetForceBatchTimeout(uint64 newforceBatchTimeout); /** * @dev Emitted when the admin update the force batch address */ event SetForceBatchAddress(address newForceBatchAddress); /** * @dev Emitted when the admin starts the two-step transfer role setting a new pending admin */ event TransferAdminRole(address newPendingAdmin); /** * @dev Emitted when the pending admin accepts the admin role */ event AcceptAdminRole(address newAdmin); // General parameters that will have in common all networks that deploys rollup manager /** * @param _globalExitRootManager Global exit root manager address * @param _pol POL token address * @param _bridgeAddress Bridge address * @param _rollupManager Global exit root manager address */ constructor( IPolygonZkEVMGlobalExitRootV2 _globalExitRootManager, IERC20Upgradeable _pol, IPolygonZkEVMBridgeV2 _bridgeAddress, PolygonRollupManager _rollupManager ) { globalExitRootManager = _globalExitRootManager; pol = _pol; bridgeAddress = _bridgeAddress; rollupManager = _rollupManager; } /** * @param _admin Admin address * @param sequencer Trusted sequencer address * @param networkID Indicates the network identifier that will be used in the bridge * @param _gasTokenAddress Indicates the token address in mainnet that will be used as a gas token * Note if a wrapped token of the bridge is used, the original network and address of this wrapped are used instead * @param sequencerURL Trusted sequencer URL * @param _networkName L2 network name */ function initialize( address _admin, address sequencer, uint32 networkID, address _gasTokenAddress, string memory sequencerURL, string memory _networkName ) external virtual onlyRollupManager initializer { bytes memory gasTokenMetadata; if (_gasTokenAddress != address(0)) { // Ask for token metadata, the same way is enconded in the bridge // Note that this function will revert if the token is not in this network // Note that this could be a possible reentrant call, but cannot make changes on the state since are static call gasTokenMetadata = bridgeAddress.getTokenMetadata(_gasTokenAddress); // Check gas token address on the bridge ( uint32 originWrappedNetwork, address originWrappedAddress ) = bridgeAddress.wrappedTokenToTokenInfo(_gasTokenAddress); if (originWrappedNetwork != 0) { // It's a wrapped token, get the wrapped parameters gasTokenAddress = originWrappedAddress; gasTokenNetwork = originWrappedNetwork; } else { // gasTokenNetwork will be mainnet, for instance 0 gasTokenAddress = _gasTokenAddress; } } // Sequence transaction to initilize the bridge // Calculate transaction to initialize the bridge bytes memory transaction = generateInitializeTransaction( networkID, gasTokenAddress, gasTokenNetwork, gasTokenMetadata ); bytes32 currentTransactionsHash = keccak256(transaction); // Get current timestamp and global exit root uint64 currentTimestamp = uint64(block.timestamp); bytes32 lastGlobalExitRoot = globalExitRootManager .getLastGlobalExitRoot(); // Add the transaction to the sequence as if it was a force transaction bytes32 newAccInputHash = keccak256( abi.encodePacked( bytes32(0), // Current acc Input hash currentTransactionsHash, lastGlobalExitRoot, // Global exit root currentTimestamp, sequencer, blockhash(block.number - 1) ) ); lastAccInputHash = newAccInputHash; rollupManager.onSequenceBatches( uint64(1), // num total batches newAccInputHash ); // Set initialize variables admin = _admin; trustedSequencer = sequencer; trustedSequencerURL = sequencerURL; networkName = _networkName; forceBatchAddress = _admin; // Constant deployment variables forceBatchTimeout = 5 days; emit InitialSequenceBatches(transaction, lastGlobalExitRoot, sequencer); } modifier onlyAdmin() { if (admin != msg.sender) { revert OnlyAdmin(); } _; } modifier onlyTrustedSequencer() { if (trustedSequencer != msg.sender) { revert OnlyTrustedSequencer(); } _; } modifier isSenderAllowedToForceBatches() { address cacheForceBatchAddress = forceBatchAddress; if ( cacheForceBatchAddress != address(0) && cacheForceBatchAddress != msg.sender ) { revert ForceBatchNotAllowed(); } _; } modifier onlyRollupManager() { if (address(rollupManager) != msg.sender) { revert OnlyRollupManager(); } _; } ///////////////////////////////////// // Sequence/Verify batches functions //////////////////////////////////// /** * @notice Allows a sequencer to send multiple batches * @param batches Struct array which holds the necessary data to append new batches to the sequence * @param maxSequenceTimestamp Max timestamp of the sequence. This timestamp must be inside a safety range (actual + 36 seconds). * This timestamp should be equal or higher of the last block inside the sequence, otherwise this batch will be invalidated by circuit. * @param initSequencedBatch This parameter must match the current last batch sequenced. * This will be a protection for the sequencer to avoid sending undesired data * @param l2Coinbase Address that will receive the fees from L2 * note Pol is not a reentrant token */ function sequenceBatches( BatchData[] calldata batches, uint64 maxSequenceTimestamp, uint64 initSequencedBatch, address l2Coinbase ) public virtual onlyTrustedSequencer { uint256 batchesNum = batches.length; if (batchesNum == 0) { revert SequenceZeroBatches(); } if (batchesNum > _MAX_VERIFY_BATCHES) { revert ExceedMaxVerifyBatches(); } // Check max sequence timestamp inside of range if ( uint256(maxSequenceTimestamp) > (block.timestamp + TIMESTAMP_RANGE) ) { revert MaxTimestampSequenceInvalid(); } // Update global exit root if there are new deposits bridgeAddress.updateGlobalExitRoot(); // Get global batch variables bytes32 l1InfoRoot = globalExitRootManager.getRoot(); // Store storage variables in memory, to save gas, because will be overrided multiple times uint64 currentLastForceBatchSequenced = lastForceBatchSequenced; bytes32 currentAccInputHash = lastAccInputHash; // Store in a temporal variable, for avoid access again the storage slot uint64 initLastForceBatchSequenced = currentLastForceBatchSequenced; for (uint256 i = 0; i < batchesNum; i++) { // Load current sequence BatchData memory currentBatch = batches[i]; // Store the current transactions hash since can be used more than once for gas saving bytes32 currentTransactionsHash = keccak256( currentBatch.transactions ); // Check if it's a forced batch if (currentBatch.forcedTimestamp > 0) { currentLastForceBatchSequenced++; // Check forced data matches bytes32 hashedForcedBatchData = keccak256( abi.encodePacked( currentTransactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, currentBatch.forcedBlockHashL1 ) ); if ( hashedForcedBatchData != forcedBatches[currentLastForceBatchSequenced] ) { revert ForcedDataDoesNotMatch(); } // Calculate next accumulated input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentTransactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, l2Coinbase, currentBatch.forcedBlockHashL1 ) ); // Delete forceBatch data since won't be used anymore delete forcedBatches[currentLastForceBatchSequenced]; } else { // Note that forcedGlobalExitRoot and forcedBlockHashL1 remain unused and unchecked in this path // The synchronizer should be aware of that if ( currentBatch.transactions.length > _MAX_TRANSACTIONS_BYTE_LENGTH ) { revert TransactionsLengthAboveMax(); } // Calculate next accumulated input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentTransactionsHash, l1InfoRoot, maxSequenceTimestamp, l2Coinbase, bytes32(0) ) ); } } // Sanity check, should be unreachable if (currentLastForceBatchSequenced > lastForceBatch) { revert ForceBatchesOverflow(); } // Store back the storage variables lastAccInputHash = currentAccInputHash; uint256 nonForcedBatchesSequenced = batchesNum; // Check if there has been forced batches if (currentLastForceBatchSequenced != initLastForceBatchSequenced) { uint64 forcedBatchesSequenced = currentLastForceBatchSequenced - initLastForceBatchSequenced; // substract forced batches nonForcedBatchesSequenced -= forcedBatchesSequenced; // Transfer pol for every forced batch submitted pol.safeTransfer( address(rollupManager), calculatePolPerForceBatch() * (forcedBatchesSequenced) ); // Store new last force batch sequenced lastForceBatchSequenced = currentLastForceBatchSequenced; } // Pay collateral for every non-forced batch submitted pol.safeTransferFrom( msg.sender, address(rollupManager), rollupManager.getBatchFee() * nonForcedBatchesSequenced ); uint64 currentBatchSequenced = rollupManager.onSequenceBatches( uint64(batchesNum), currentAccInputHash ); // Check init sequenced batch if ( initSequencedBatch != (currentBatchSequenced - uint64(batchesNum)) ) { revert InitSequencedBatchDoesNotMatch(); } emit SequenceBatches(currentBatchSequenced, l1InfoRoot); } /** * @notice Callback on verify batches, can only be called by the rollup manager * @param lastVerifiedBatch Last verified batch * @param newStateRoot new state root * @param aggregator Aggregator address */ function onVerifyBatches( uint64 lastVerifiedBatch, bytes32 newStateRoot, address aggregator ) public virtual override onlyRollupManager { emit VerifyBatches(lastVerifiedBatch, newStateRoot, aggregator); } //////////////////////////// // Force batches functions //////////////////////////// /** * @notice Allows a sequencer/user to force a batch of L2 transactions. * This should be used only in extreme cases where the trusted sequencer does not work as expected * Note The sequencer has certain degree of control on how non-forced and forced batches are ordered * In order to assure that users force transactions will be processed properly, user must not sign any other transaction * with the same nonce * @param transactions L2 ethereum transactions EIP-155 or pre-EIP-155 with signature: * @param polAmount Max amount of pol tokens that the sender is willing to pay */ function forceBatch( bytes calldata transactions, uint256 polAmount ) public virtual isSenderAllowedToForceBatches { // Check if rollup manager is on emergency state if (rollupManager.isEmergencyState()) { revert ForceBatchesNotAllowedOnEmergencyState(); } // Calculate pol collateral uint256 polFee = rollupManager.getForcedBatchFee(); if (polFee > polAmount) { revert NotEnoughPOLAmount(); } if (transactions.length > _MAX_FORCE_BATCH_BYTE_LENGTH) { revert TransactionsLengthAboveMax(); } // keep the pol fees on this contract until forced it's sequenced pol.safeTransferFrom(msg.sender, address(this), polFee); // Get globalExitRoot global exit root bytes32 lastGlobalExitRoot = globalExitRootManager .getLastGlobalExitRoot(); // Update forcedBatches mapping lastForceBatch++; forcedBatches[lastForceBatch] = keccak256( abi.encodePacked( keccak256(transactions), lastGlobalExitRoot, uint64(block.timestamp), blockhash(block.number - 1) ) ); if (msg.sender == tx.origin) { // Getting the calldata from an EOA is easy so no need to put the `transactions` in the event emit ForceBatch(lastForceBatch, lastGlobalExitRoot, msg.sender, ""); } else { // Getting internal transaction calldata is complicated (because it requires an archive node) // Therefore it's worth it to put the `transactions` in the event, which is easy to query emit ForceBatch( lastForceBatch, lastGlobalExitRoot, msg.sender, transactions ); } } /** * @notice Allows anyone to sequence forced Batches if the trusted sequencer has not done so in the timeout period * @param batches Struct array which holds the necessary data to append force batches */ function sequenceForceBatches( BatchData[] calldata batches ) external virtual isSenderAllowedToForceBatches { // Check if rollup manager is on emergency state if ( rollupManager.lastDeactivatedEmergencyStateTimestamp() + _HALT_AGGREGATION_TIMEOUT > block.timestamp ) { revert HaltTimeoutNotExpiredAfterEmergencyState(); } uint256 batchesNum = batches.length; if (batchesNum == 0) { revert SequenceZeroBatches(); } if (batchesNum > _MAX_VERIFY_BATCHES) { revert ExceedMaxVerifyBatches(); } if ( uint256(lastForceBatchSequenced) + batchesNum > uint256(lastForceBatch) ) { revert ForceBatchesOverflow(); } // Store storage variables in memory, to save gas, because will be overrided multiple times uint64 currentLastForceBatchSequenced = lastForceBatchSequenced; bytes32 currentAccInputHash = lastAccInputHash; // Sequence force batches for (uint256 i = 0; i < batchesNum; i++) { // Load current sequence BatchData memory currentBatch = batches[i]; currentLastForceBatchSequenced++; // Store the current transactions hash since it's used more than once for gas saving bytes32 currentTransactionsHash = keccak256( currentBatch.transactions ); // Check forced data matches bytes32 hashedForcedBatchData = keccak256( abi.encodePacked( currentTransactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, currentBatch.forcedBlockHashL1 ) ); if ( hashedForcedBatchData != forcedBatches[currentLastForceBatchSequenced] ) { revert ForcedDataDoesNotMatch(); } // Delete forceBatch data since won't be used anymore delete forcedBatches[currentLastForceBatchSequenced]; if (i == (batchesNum - 1)) { // The last batch will have the most restrictive timestamp if ( currentBatch.forcedTimestamp + forceBatchTimeout > block.timestamp ) { revert ForceBatchTimeoutNotExpired(); } } // Calculate next acc input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentTransactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, msg.sender, currentBatch.forcedBlockHashL1 ) ); } // Transfer pol for every forced batch submitted pol.safeTransfer( address(rollupManager), calculatePolPerForceBatch() * (batchesNum) ); // Store back the storage variables lastAccInputHash = currentAccInputHash; lastForceBatchSequenced = currentLastForceBatchSequenced; uint64 currentBatchSequenced = rollupManager.onSequenceBatches( uint64(batchesNum), currentAccInputHash ); emit SequenceForceBatches(currentBatchSequenced); } ////////////////// // admin functions ////////////////// /** * @notice Allow the admin to set a new trusted sequencer * @param newTrustedSequencer Address of the new trusted sequencer */ function setTrustedSequencer( address newTrustedSequencer ) external onlyAdmin { trustedSequencer = newTrustedSequencer; emit SetTrustedSequencer(newTrustedSequencer); } /** * @notice Allow the admin to set the trusted sequencer URL * @param newTrustedSequencerURL URL of trusted sequencer */ function setTrustedSequencerURL( string memory newTrustedSequencerURL ) external onlyAdmin { trustedSequencerURL = newTrustedSequencerURL; emit SetTrustedSequencerURL(newTrustedSequencerURL); } /** * @notice Allow the admin to change the force batch address, that will be allowed to force batches * If address 0 is set, then everyone is able to force batches, this action is irreversible * @param newForceBatchAddress New force batch address */ function setForceBatchAddress( address newForceBatchAddress ) external onlyAdmin { if (forceBatchAddress == address(0)) { revert ForceBatchesDecentralized(); } forceBatchAddress = newForceBatchAddress; emit SetForceBatchAddress(newForceBatchAddress); } /** * @notice Allow the admin to set the forcedBatchTimeout * The new value can only be lower, except if emergency state is active * @param newforceBatchTimeout New force batch timeout */ function setForceBatchTimeout( uint64 newforceBatchTimeout ) external onlyAdmin { if (newforceBatchTimeout > _HALT_AGGREGATION_TIMEOUT) { revert InvalidRangeForceBatchTimeout(); } if (!rollupManager.isEmergencyState()) { if (newforceBatchTimeout >= forceBatchTimeout) { revert InvalidRangeForceBatchTimeout(); } } forceBatchTimeout = newforceBatchTimeout; emit SetForceBatchTimeout(newforceBatchTimeout); } /** * @notice Starts the admin role transfer * This is a two step process, the pending admin must accepted to finalize the process * @param newPendingAdmin Address of the new pending admin */ function transferAdminRole(address newPendingAdmin) external onlyAdmin { pendingAdmin = newPendingAdmin; emit TransferAdminRole(newPendingAdmin); } /** * @notice Allow the current pending admin to accept the admin role */ function acceptAdminRole() external { if (pendingAdmin != msg.sender) { revert OnlyPendingAdmin(); } admin = pendingAdmin; emit AcceptAdminRole(pendingAdmin); } ////////////////// // view/pure functions ////////////////// /** * @notice Function to calculate the reward for a forced batch */ function calculatePolPerForceBatch() public view returns (uint256) { uint256 currentBalance = pol.balanceOf(address(this)); // Pending forced Batches = last forced batch added - last forced batch sequenced uint256 pendingForcedBatches = lastForceBatch - lastForceBatchSequenced; if (pendingForcedBatches == 0) return 0; return currentBalance / pendingForcedBatches; } /** * @notice Generate Initialize transaction for hte bridge on L2 * @param networkID Indicates the network identifier that will be used in the bridge * @param _gasTokenAddress Indicates the token address that will be used to pay gas fees in the new rollup * @param _gasTokenNetwork Indicates the native network of the token address * @param _gasTokenMetadata Abi encoded gas token metadata */ function generateInitializeTransaction( uint32 networkID, address _gasTokenAddress, uint32 _gasTokenNetwork, bytes memory _gasTokenMetadata ) public view returns (bytes memory) { bytes memory initializeBrigeData = abi.encodeCall( IPolygonZkEVMBridgeV2.initialize, ( networkID, _gasTokenAddress, _gasTokenNetwork, GLOBAL_EXIT_ROOT_MANAGER_L2, address(0), // Rollup manager on L2 does not exist _gasTokenMetadata ) ); bytes memory bytesToSign; if (_gasTokenMetadata.length == 0) { bytesToSign = abi.encodePacked( INITIALIZE_TX_BRIDGE_LIST_LEN_LEN, uint16(initializeBrigeData.length) + INITIALIZE_TX_CONSTANT_BYTES_EMPTY_METADATA, // do not support more than 2 bytes of length, intended to revert on overflow INITIALIZE_TX_BRIDGE_PARAMS, bridgeAddress, INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS_EMPTY_METADATA, INITIALIZE_TX_DATA_LEN_EMPTY_METADATA, initializeBrigeData ); } else { // Do not support more than 65535 bytes if (initializeBrigeData.length > type(uint16).max) { revert HugeTokenMetadataNotSupported(); } uint16 initializeBrigeDataLen = uint16(initializeBrigeData.length); bytesToSign = abi.encodePacked( INITIALIZE_TX_BRIDGE_LIST_LEN_LEN, uint16(initializeBrigeData.length) + INITIALIZE_TX_CONSTANT_BYTES, // do not support more than 2 bytes of length, intended to revert on overflow INITIALIZE_TX_BRIDGE_PARAMS, bridgeAddress, INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS, initializeBrigeDataLen, initializeBrigeData ); } // Sanity check that the ecrecover will work // Should never happen that giving a valid signature, ecrecover "breaks" address signer = ecrecover( keccak256(bytesToSign), SIGNATURE_INITIALIZE_TX_V, SIGNATURE_INITIALIZE_TX_R, SIGNATURE_INITIALIZE_TX_S ); if (signer == address(0)) { revert InvalidInitializeTransaction(); } bytes memory transaction = abi.encodePacked( bytesToSign, SIGNATURE_INITIALIZE_TX_R, SIGNATURE_INITIALIZE_TX_S, SIGNATURE_INITIALIZE_TX_V, INITIALIZE_TX_EFFECTIVE_PERCENTAGE ); return transaction; } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.20; import "../../lib/PolygonRollupBaseEtrog.sol"; /** * Contract responsible for managing the states and the updates of L2 network. * There will be a trusted sequencer, which is able to send transactions. * Any user can force some transaction and the sequencer will have a timeout to add them in the queue. * The sequenced state is deterministic and can be precalculated before it's actually verified by a zkProof. * The aggregators will be able to verify the sequenced state with zkProofs and therefore make available the withdrawals from L2 network. * To enter and exit of the L2 network will be used a PolygonZkEVMBridge smart contract that will be deployed in both networks. */ contract PolygonZkEVMExistentEtrog is PolygonRollupBaseEtrog { // Transaction that will be injected as a forced transaction, to setup the timestamp on the state root, we just need a well encoded RLP transaction // It's ok if the transaction is not processable /* Encoded transaction: { "from": "0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D", "to": "0x4d5Cf5032B2a844602278b01199ED191A86c93ff", "nonce": 42, "data": "0x", "value": "0", "gasLimit": 0, "gasPrice": "0", "chainId": 4242, "overwrite": { "v": "0x1b", "r": "0x00000000000000000000000000000000000000000000000000000005ca1ab1e0", "s": "0x00000000000000000000000000000000000000000000000000000005ca1ab1e0" } } */ bytes public constant SET_UP_ETROG_TX = hex"df2a8080944d5cf5032b2a844602278b01199ed191a86c93ff8080821092808000000000000000000000000000000000000000000000000000000005ca1ab1e000000000000000000000000000000000000000000000000000000005ca1ab1e01bff"; /** * @dev Emitted when the system is updated to a etrog using this contract, contain the set up etrog transaction */ event UpdateEtrogSequence( uint64 numBatch, bytes transactions, bytes32 lastGlobalExitRoot, address sequencer ); /** * @param _globalExitRootManager Global exit root manager address * @param _pol POL token address * @param _bridgeAddress Bridge address * @param _rollupManager Global exit root manager address */ constructor( IPolygonZkEVMGlobalExitRootV2 _globalExitRootManager, IERC20Upgradeable _pol, IPolygonZkEVMBridgeV2 _bridgeAddress, PolygonRollupManager _rollupManager ) PolygonRollupBaseEtrog( _globalExitRootManager, _pol, _bridgeAddress, _rollupManager ) {} /** * note This initializer will be called instead of the PolygonRollupBase * This is a especial initializer since the zkEVM it's an already created network * @param _admin Admin address * @param _trustedSequencer Trusted sequencer address * @param _trustedSequencerURL Trusted sequencer URL * @param _networkName L2 network name * @param _lastAccInputHash Acc input hash */ function initializeUpgrade( address _admin, address _trustedSequencer, string memory _trustedSequencerURL, string memory _networkName, bytes32 _lastAccInputHash ) external onlyRollupManager initializer { // Set up etrog Tx bytes memory transaction = SET_UP_ETROG_TX; bytes32 currentTransactionsHash = keccak256(transaction); // Get current timestamp and global exit root uint64 currentTimestamp = uint64(block.timestamp); bytes32 lastGlobalExitRoot = globalExitRootManager .getLastGlobalExitRoot(); // Add the transaction to the sequence as if it was a force transaction bytes32 newAccInputHash = keccak256( abi.encodePacked( _lastAccInputHash, // Last acc Input hash currentTransactionsHash, lastGlobalExitRoot, // Global exit root currentTimestamp, _trustedSequencer, blockhash(block.number - 1) ) ); // Set acumulated input hash lastAccInputHash = newAccInputHash; uint64 currentBatchSequenced = rollupManager.onSequenceBatches( uint64(1), // num total batches newAccInputHash ); // Set zkEVM variables admin = _admin; trustedSequencer = _trustedSequencer; trustedSequencerURL = _trustedSequencerURL; networkName = _networkName; forceBatchAddress = _admin; // Constant variables forceBatchTimeout = 5 days; // Both gasTokenAddress and gasTokenNetwork are 0, since it uses ether as gas token // Therefore is not necessary to set the variables emit UpdateEtrogSequence( currentBatchSequenced, transaction, lastGlobalExitRoot, _trustedSequencer ); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; interface IDataAvailabilityProtocol { function getProcotolName() external view returns (string memory); function verifyMessage( bytes32 hash, bytes calldata dataAvailabilityMessage ) external view; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; interface IPolygonRollupBase { function initialize( address _admin, address sequencer, uint32 networkID, address gasTokenAddress, string memory sequencerURL, string memory _networkName ) external; function onVerifyBatches( uint64 lastVerifiedBatch, bytes32 newStateRoot, address aggregator ) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; interface IPolygonRollupManager { /** * @dev Thrown when sender is not the PolygonZkEVM address */ error UpdateToSameRollupTypeID(); /** * @dev Thrown when sender is not the PolygonZkEVM address */ error RollupMustExist(); /** * @dev Thrown when sender is not the PolygonZkEVM address */ error SenderMustBeRollup(); /** * @dev Thrown when sender is not the PolygonZkEVM address */ error TrustedAggregatorTimeoutNotExpired(); /** * @dev Thrown when sender is not the PolygonZkEVM address */ error ExceedMaxVerifyBatches(); /** * @dev Thrown when attempting to access a pending state that does not exist */ error PendingStateDoesNotExist(); /** * @dev Thrown when the init num batch does not match with the one in the pending state */ error InitNumBatchDoesNotMatchPendingState(); /** * @dev Thrown when the old state root of a certain batch does not exist */ error OldStateRootDoesNotExist(); /** * @dev Thrown when the init verification batch is above the last verification batch */ error InitNumBatchAboveLastVerifiedBatch(); /** * @dev Thrown when the final verification batch is below or equal the last verification batch */ error FinalNumBatchBelowLastVerifiedBatch(); /** * @dev Thrown when the zkproof is not valid */ error InvalidProof(); /** * @dev Thrown when attempting to consolidate a pending state not yet consolidable */ error PendingStateNotConsolidable(); /** * @dev Thrown when attempting to consolidate a pending state that is already consolidated or does not exist */ error PendingStateInvalid(); /** * @dev Thrown when the new accumulate input hash does not exist */ error NewAccInputHashDoesNotExist(); /** * @dev Thrown when the new state root is not inside prime */ error NewStateRootNotInsidePrime(); /** * @dev Thrown when the final pending state num is not in a valid range */ error FinalPendingStateNumInvalid(); /** * @dev Thrown when the final num batch does not match with the one in the pending state */ error FinalNumBatchDoesNotMatchPendingState(); /** * @dev Thrown when the stored root matches the new root proving a different state */ error StoredRootMustBeDifferentThanNewRoot(); /** * @dev Thrown when the halt timeout is not expired when attempting to activate the emergency state */ error HaltTimeoutNotExpired(); /** * @dev Thrown when the old accumulate input hash does not exist */ error OldAccInputHashDoesNotExist(); /** * @dev Thrown when attempting to set a new trusted aggregator timeout equal or bigger than current one */ error NewTrustedAggregatorTimeoutMustBeLower(); /** * @dev Thrown when attempting to set a new pending state timeout equal or bigger than current one */ error NewPendingStateTimeoutMustBeLower(); /** * @dev Thrown when attempting to set a new multiplier batch fee in a invalid range of values */ error InvalidRangeMultiplierBatchFee(); /** * @dev Thrown when attempting to set a batch time target in an invalid range of values */ error InvalidRangeBatchTimeTarget(); /** * @dev Thrown when the caller is not the pending admin */ error ChainIDAlreadyExist(); /** * @dev Thrown when the caller is not the pending admin */ error MustSequenceSomeBatch(); /** * @dev When a rollup type does not exist */ error RollupTypeDoesNotExist(); /** * @dev When a rollup type does not exist */ error RollupTypeObsolete(); /** * @dev When a rollup type does not exist */ error InitBatchMustMatchCurrentForkID(); /** * @dev When a rollup type does not exist */ error UpdateNotCompatible(); /** * @dev When a rollup type does not exist */ error BatchFeeOutOfRange(); /** * @dev When a rollup type does not exist */ error AllzkEVMSequencedBatchesMustBeVerified(); /** * @dev When adding an existing rollup where the rollup address already was added */ error RollupAddressAlreadyExist(); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; interface IPolygonValidium { /** * @dev Thrown when try to activate force batches when they are already active */ error SequenceWithDataAvailabilityNotAllowed(); /** * @dev Thrown when try to switch SequenceWithDataAvailability to the same value */ error SwitchToSameValue(); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; import "../../interfaces/IBasePolygonZkEVMGlobalExitRoot.sol"; interface IPolygonZkEVMBridgeV2 { /** * @dev Thrown when the destination network is invalid */ error DestinationNetworkInvalid(); /** * @dev Thrown when the amount does not match msg.value */ error AmountDoesNotMatchMsgValue(); /** * @dev Thrown when user is bridging tokens and is also sending a value */ error MsgValueNotZero(); /** * @dev Thrown when the Ether transfer on claimAsset fails */ error EtherTransferFailed(); /** * @dev Thrown when the message transaction on claimMessage fails */ error MessageFailed(); /** * @dev Thrown when the global exit root does not exist */ error GlobalExitRootInvalid(); /** * @dev Thrown when the smt proof does not match */ error InvalidSmtProof(); /** * @dev Thrown when an index is already claimed */ error AlreadyClaimed(); /** * @dev Thrown when the owner of permit does not match the sender */ error NotValidOwner(); /** * @dev Thrown when the spender of the permit does not match this contract address */ error NotValidSpender(); /** * @dev Thrown when the amount of the permit does not match */ error NotValidAmount(); /** * @dev Thrown when the permit data contains an invalid signature */ error NotValidSignature(); /** * @dev Thrown when sender is not the rollup manager */ error OnlyRollupManager(); /** * @dev Thrown when the permit data contains an invalid signature */ error NativeTokenIsEther(); /** * @dev Thrown when the permit data contains an invalid signature */ error NoValueInMessagesOnGasTokenNetworks(); /** * @dev Thrown when the permit data contains an invalid signature */ error GasTokenNetworkMustBeZeroOnEther(); /** * @dev Thrown when the wrapped token deployment fails */ error FailedTokenWrappedDeployment(); function wrappedTokenToTokenInfo( address destinationAddress ) external view returns (uint32, address); function updateGlobalExitRoot() external; function activateEmergencyState() external; function deactivateEmergencyState() external; function bridgeAsset( uint32 destinationNetwork, address destinationAddress, uint256 amount, address token, bool forceUpdateGlobalExitRoot, bytes calldata permitData ) external payable; function bridgeMessage( uint32 destinationNetwork, address destinationAddress, bool forceUpdateGlobalExitRoot, bytes calldata metadata ) external payable; function bridgeMessageWETH( uint32 destinationNetwork, address destinationAddress, uint256 amountWETH, bool forceUpdateGlobalExitRoot, bytes calldata metadata ) external; function claimAsset( bytes32[32] calldata smtProofLocalExitRoot, bytes32[32] calldata smtProofRollupExitRoot, uint256 globalIndex, bytes32 mainnetExitRoot, bytes32 rollupExitRoot, uint32 originNetwork, address originTokenAddress, uint32 destinationNetwork, address destinationAddress, uint256 amount, bytes calldata metadata ) external; function claimMessage( bytes32[32] calldata smtProofLocalExitRoot, bytes32[32] calldata smtProofRollupExitRoot, uint256 globalIndex, bytes32 mainnetExitRoot, bytes32 rollupExitRoot, uint32 originNetwork, address originAddress, uint32 destinationNetwork, address destinationAddress, uint256 amount, bytes calldata metadata ) external; function initialize( uint32 _networkID, address _gasTokenAddress, uint32 _gasTokenNetwork, IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager, address _polygonRollupManager, bytes memory _gasTokenMetadata ) external; function getTokenMetadata( address token ) external view returns (bytes memory); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; import "../../interfaces/IBasePolygonZkEVMGlobalExitRoot.sol"; interface IPolygonZkEVMGlobalExitRootV2 is IBasePolygonZkEVMGlobalExitRoot { function getLastGlobalExitRoot() external view returns (bytes32); function getRoot() external view returns (bytes32); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; import "../../interfaces/IPolygonZkEVMErrors.sol"; interface IPolygonZkEVMVEtrogErrors is IPolygonZkEVMErrors { /** * @dev Thrown when the caller is not the trusted sequencer */ error OnlyRollupManager(); /** * @dev Thrown when the caller is not the trusted sequencer */ error NotEnoughPOLAmount(); /** * @dev Thrown when the caller is not the trusted sequencer */ error InvalidInitializeTransaction(); /** * @dev Thrown when the caller is not the trusted sequencer */ error GasTokenNetworkMustBeZeroOnEther(); /** * @dev Thrown when the try to initialize with a gas token with huge metadata */ error HugeTokenMetadataNotSupported(); /** * @dev Thrown when trying force a batch during emergency state */ error ForceBatchesNotAllowedOnEmergencyState(); /** * @dev Thrown when the try to sequence force batches before the halt timeout period */ error HaltTimeoutNotExpiredAfterEmergencyState(); /** * @dev Thrown when the try to update the force batch address once is set to address(0) */ error ForceBatchesDecentralized(); /** * @dev Thrown when the last sequenced batch nmber does not match the init sequeced batch number */ error InitSequencedBatchDoesNotMatch(); /** * @dev Thrown when the max timestamp is out of range */ error MaxTimestampSequenceInvalid(); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; /** * Since the current contract of PolygonZkEVM will be upgraded to a PolygonRollupManager there's defined * all the legacy public variables in order to not use previous used storage slots * The variables will be used by the RollupManager only for initialize the zkEVM inside the initializer function */ contract LegacyZKEVMStateVariables { /** * @notice Struct which will be stored for every batch sequence * @param accInputHash Hash chain that contains all the information to process a batch: * Before etrog: keccak256(bytes32 oldAccInputHash, keccak256(bytes transactions), bytes32 globalExitRoot, uint64 timestamp, address seqAddress) * Etrog: keccak256(bytes32 oldAccInputHash, keccak256(bytes transactions), bytes32 l1InfoRoot/forcedGlobalExitRoot, uint64 currentTimestamp/forcedTimestamp, address l2Coinbase, bytes32 0/forcedBlockHashL1) * @param sequencedTimestamp Sequenced timestamp * @param previousLastBatchSequenced Previous last batch sequenced before the current one, this is used to properly calculate the fees */ struct SequencedBatchData { bytes32 accInputHash; uint64 sequencedTimestamp; uint64 previousLastBatchSequenced; } /** * @notice Struct to store the pending states * Pending state will be an intermediary state, that after a timeout can be consolidated, which means that will be added * to the state root mapping, and the global exit root will be updated * This is a protection mechanism against soundness attacks, that will be turned off in the future * @param timestamp Timestamp where the pending state is added to the queue * @param lastVerifiedBatch Last batch verified batch of this pending state * @param exitRoot Pending exit root * @param stateRoot Pending state root */ struct PendingState { uint64 timestamp; uint64 lastVerifiedBatch; bytes32 exitRoot; bytes32 stateRoot; } // Time target of the verification of a batch // Adaptatly the batchFee will be updated to achieve this target /// @custom:oz-renamed-from verifyBatchTimeTarget uint64 internal _legacyVerifyBatchTimeTarget; // Batch fee multiplier with 3 decimals that goes from 1000 - 1023 /// @custom:oz-renamed-from multiplierBatchFee uint16 internal _legacyMultiplierBatchFee; // Trusted sequencer address /// @custom:oz-renamed-from trustedSequencer address internal _legacyTrustedSequencer; // Current matic fee per batch sequenced /// @custom:oz-renamed-from batchFee uint256 internal _legacyBatchFee; // Queue of forced batches with their associated data // ForceBatchNum --> hashedForcedBatchData // hashedForcedBatchData: hash containing the necessary information to force a batch: // keccak256(keccak256(bytes transactions), bytes32 globalExitRoot, unint64 minForcedTimestamp) /// @custom:oz-renamed-from forcedBatches mapping(uint64 => bytes32) internal _legacyForcedBatches; // Queue of batches that defines the virtual state // SequenceBatchNum --> SequencedBatchData /// @custom:oz-renamed-from sequencedBatches mapping(uint64 => SequencedBatchData) internal _legacySequencedBatches; // Last sequenced timestamp /// @custom:oz-renamed-from lastTimestamp uint64 internal _legacyLastTimestamp; // Last batch sent by the sequencers /// @custom:oz-renamed-from lastBatchSequenced uint64 internal _legacylastBatchSequenced; // Last forced batch included in the sequence /// @custom:oz-renamed-from lastForceBatchSequenced uint64 internal _legacyLastForceBatchSequenced; // Last forced batch /// @custom:oz-renamed-from lastForceBatch uint64 internal _legacyLastForceBatch; // Last batch verified by the aggregators /// @custom:oz-renamed-from lastVerifiedBatch uint64 internal _legacyLastVerifiedBatch; // Trusted aggregator address /// @custom:oz-renamed-from trustedAggregator address internal _legacyTrustedAggregator; // State root mapping // BatchNum --> state root /// @custom:oz-renamed-from batchNumToStateRoot mapping(uint64 => bytes32) internal _legacyBatchNumToStateRoot; // Trusted sequencer URL /// @custom:oz-renamed-from trustedSequencerURL string internal _legacyTrustedSequencerURL; // L2 network name /// @custom:oz-renamed-from networkName string internal _legacyNetworkName; // Pending state mapping // pendingStateNumber --> PendingState /// @custom:oz-renamed-from pendingStateTransitions mapping(uint256 => PendingState) internal _legacyPendingStateTransitions; // Last pending state /// @custom:oz-renamed-from lastPendingState uint64 internal _legacyLastPendingState; // Last pending state consolidated /// @custom:oz-renamed-from lastPendingStateConsolidated uint64 internal _legacyLastPendingStateConsolidated; // Once a pending state exceeds this timeout it can be consolidated /// @custom:oz-renamed-from pendingStateTimeout uint64 internal _legacyPendingStateTimeout; // Trusted aggregator timeout, if a sequence is not verified in this time frame, // everyone can verify that sequence /// @custom:oz-renamed-from trustedAggregatorTimeout uint64 internal _legacyTrustedAggregatorTimeout; // Address that will be able to adjust contract parameters or stop the emergency state /// @custom:oz-renamed-from admin address internal _legacyAdmin; // This account will be able to accept the admin role /// @custom:oz-renamed-from pendingAdmin address internal _legacyPendingAdmin; // Force batch timeout /// @custom:oz-renamed-from forceBatchTimeout uint64 internal _legacyForceBatchTimeout; // Indicates if forced batches are disallowed /// @custom:oz-renamed-from isForcedBatchDisallowed bool internal _legacyIsForcedBatchDisallowed; // Indicates the current version /// @custom:oz-renamed-from version uint256 internal _legacyVersion; // Last batch verified before the last upgrade /// @custom:oz-renamed-from lastVerifiedBatchBeforeUpgrade uint256 internal _legacyLastVerifiedBatchBeforeUpgrade; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; /** * @dev Contract AccessControlUpgradeable from Openzeppelin with the following modifications: * - Delete ERC165Upgradeable dependencies, which is not important to our contract and save us the "gap" * variables and let us have consistent storage * - Add the legacy Owner variable, to be consistent with the previous one * - Add custom errors * - Replace _msgSender() with msg.sender */ abstract contract PolygonAccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable { function __AccessControl_init() internal onlyInitializing {} // Legacy variable /// @custom:oz-renamed-from _owner address internal _legacyOwner; struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Thrown when the addres does not have the required role */ error AddressDoNotHaveRequiredRole(); /** * @dev Thrown when the renounce address is not the message sender */ error AccessControlOnlyCanRenounceRolesForSelf(); /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole( bytes32 role, address account ) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `msg.sender` is missing `role`. * Overriding this function changes the behavior of the {onlyRole} modifier. * * Format of the revert message is described in {_checkRole}. * * _Available since v4.6._ */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, msg.sender); } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AddressDoNotHaveRequiredRole(); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin( bytes32 role ) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole( bytes32 role, address account ) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole( bytes32 role, address account ) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. * * May emit a {RoleRevoked} event. */ function renounceRole( bytes32 role, address account ) public virtual override { if (account != msg.sender) { revert AccessControlOnlyCanRenounceRolesForSelf(); } _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * May emit a {RoleGranted} event. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, msg.sender); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, msg.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[48] private __gap; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; /** * This contract will contain the constants used across different contracts */ contract PolygonConstantsBase { // If the system a does not verify a batch inside this time window, the contract enters in emergency mode uint64 internal constant _HALT_AGGREGATION_TIMEOUT = 1 weeks; // Maximum batches that can be verified in one call. It depends on our current metrics // This should be a protection against someone that tries to generate huge chunk of invalid batches, and we can't prove otherwise before the pending timeout expires uint64 internal constant _MAX_VERIFY_BATCHES = 1000; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.20; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "../interfaces/IPolygonZkEVMGlobalExitRootV2.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../../interfaces/IPolygonZkEVMErrors.sol"; import "../interfaces/IPolygonZkEVMVEtrogErrors.sol"; import "../PolygonRollupManager.sol"; import "../interfaces/IPolygonRollupBase.sol"; import "../interfaces/IPolygonZkEVMBridgeV2.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; import "./PolygonConstantsBase.sol"; /** * Contract responsible for managing the states and the updates of L2 network. * There will be a trusted sequencer, which is able to send transactions. * Any user can force some transaction and the sequencer will have a timeout to add them in the queue. * The sequenced state is deterministic and can be precalculated before it's actually verified by a zkProof. * The aggregators will be able to verify the sequenced state with zkProofs and therefore make available the withdrawals from L2 network. * To enter and exit of the L2 network will be used a PolygonZkEVMBridge smart contract that will be deployed in both networks. */ abstract contract PolygonRollupBaseEtrog is Initializable, PolygonConstantsBase, IPolygonZkEVMVEtrogErrors, IPolygonRollupBase { using SafeERC20Upgradeable for IERC20Upgradeable; /** * @notice Struct which will be used to call sequenceBatches * @param transactions L2 ethereum transactions EIP-155 or pre-EIP-155 with signature: * EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data, chainid, 0, 0,) || v || r || s * pre-EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data) || v || r || s * @param forcedGlobalExitRoot Global exit root, empty when sequencing a non forced batch * @param forcedTimestamp Minimum timestamp of the force batch data, empty when sequencing a non forced batch * @param forcedBlockHashL1 blockHash snapshot of the force batch data, empty when sequencing a non forced batch */ struct BatchData { bytes transactions; bytes32 forcedGlobalExitRoot; uint64 forcedTimestamp; bytes32 forcedBlockHashL1; } // Max transactions bytes that can be added in a single batch // Max keccaks circuit = (2**23 / 155286) * 44 = 2376 // Bytes per keccak = 136 // Minimum Static keccaks batch = 2 // Max bytes allowed = (2376 - 2) * 136 = 322864 bytes - 1 byte padding // Rounded to 300000 bytes // In order to process the transaction, the data is approximately hashed twice for ecrecover: // 300000 bytes / 2 = 150000 bytes // Since geth pool currently only accepts at maximum 128kb transactions: // https://github.com/ethereum/go-ethereum/blob/master/core/txpool/txpool.go#L54 // We will limit this length to be compliant with the geth restrictions since our node will use it // We let 8kb as a sanity margin uint256 internal constant _MAX_TRANSACTIONS_BYTE_LENGTH = 120000; // Max force batch transaction length // This is used to avoid huge calldata attacks, where the attacker call force batches from another contract uint256 internal constant _MAX_FORCE_BATCH_BYTE_LENGTH = 5000; // In order to encode the initialize transaction of the bridge there's have a constant part and the metadata which is variable // Note the total transaction will be constrained to 65535 to avoid attacks and simplify the implementation // List rlp: 1 listLenLen "0xf9" (0xf7 + 2), + listLen 2 (32 bytes + txData bytes) (do not accept more than 65535 bytes) // First byte of the initialize bridge tx, indicates a list with a lengt of 2 bytes // Since the minimum constant bytes will be: 259 (tx data empty) + 31 (tx parameters) = 259 (0x103) will always take 2 bytes to express the lenght of the rlp // Note that more than 2 bytes of list len is not supported, since it's constrained to 65535 uint8 public constant INITIALIZE_TX_BRIDGE_LIST_LEN_LEN = 0xf9; // Tx parameters until the bridge address bytes public constant INITIALIZE_TX_BRIDGE_PARAMS = hex"80808401c9c38094"; // RLP encoded metadata (non empty) // TxData bytes: 164 bytes data ( signature 4 bytes + 5 parameters*32bytes + // (abi encoded metadata: 32 bytes position + 32 bytes len + 32 bytes position name + 32 bytes length name + 32 bytes position Symbol + 32 bytes length Symbol //+ 32 bytes decimal )) min 7*32 bytes = // = 164 bytes + 224 bytes = 388 (0x0184) minimum // Extra data: nameLen padded to 32 bytes + symbol len padded to 32 bytes // Constant bytes: 1 nonce "0x80" + 1 gasPrice "0x80" + 5 gasLimit "0x8401c9c380" (30M gas) // + 21 to ("0x94" + bridgeAddress") + 1 value "0x80" + 1 stringLenLen "0xb9" (0xb7 + 2) + // stringLen (0x0184 + nameLen padded to 32 bytes + symbol len padded to 32 bytes) + txData bytes = 32 bytes + txData bytes uint16 public constant INITIALIZE_TX_CONSTANT_BYTES = 32; // Tx parameters after the bridge address bytes public constant INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS = hex"80b9"; // RLP empty metadata // TxData empty metadata bytes: 164 bytes data ( signature 4 bytes + 5 parameters*32bytes + // (abi encoded metadata: 32 bytes position + 32 bytes len = 2*32 bytes = // = 164 bytes + 64 bytes = 228 (0xe4) // Constant bytes empty metadata : 1 nonce "0x80" + 1 gasPrice "0x80" + 5 gasLimit "0x8401c9c380" (30M gas) // + 21 to ("0x94" + bridgeAddress") + 1 value "0x80" + 1 stringLenLen "0xb8" (0xb7 + 1) + // 1 stringLen (0xe4) + txData bytes = 31 bytes + txData bytes empty metadata 228 = 259 uint16 public constant INITIALIZE_TX_CONSTANT_BYTES_EMPTY_METADATA = 31; uint8 public constant INITIALIZE_TX_DATA_LEN_EMPTY_METADATA = 228; // 0xe4 // Tx parameters after the bridge address bytes public constant INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS_EMPTY_METADATA = hex"80b8"; // Signature used to initialize the bridge // V parameter of the initialize signature uint8 public constant SIGNATURE_INITIALIZE_TX_V = 27; // R parameter of the initialize signature bytes32 public constant SIGNATURE_INITIALIZE_TX_R = 0x00000000000000000000000000000000000000000000000000000005ca1ab1e0; // S parameter of the initialize signature bytes32 public constant SIGNATURE_INITIALIZE_TX_S = 0x000000000000000000000000000000000000000000000000000000005ca1ab1e; // Effective percentage of the initalize transaction bytes1 public constant INITIALIZE_TX_EFFECTIVE_PERCENTAGE = 0xFF; // Global Exit Root address L2 IBasePolygonZkEVMGlobalExitRoot public constant GLOBAL_EXIT_ROOT_MANAGER_L2 = IBasePolygonZkEVMGlobalExitRoot( 0xa40D5f56745a118D0906a34E69aeC8C0Db1cB8fA ); // Timestamp range that's given to the sequencer as a safety measure to avoid reverts if the transaction is mined to quickly uint256 public constant TIMESTAMP_RANGE = 36; // POL token address IERC20Upgradeable public immutable pol; // Global Exit Root interface IPolygonZkEVMGlobalExitRootV2 public immutable globalExitRootManager; // PolygonZkEVM Bridge Address IPolygonZkEVMBridgeV2 public immutable bridgeAddress; // Rollup manager PolygonRollupManager public immutable rollupManager; // Address that will be able to adjust contract parameters address public admin; // This account will be able to accept the admin role address public pendingAdmin; // Trusted sequencer address address public trustedSequencer; // Trusted sequencer URL string public trustedSequencerURL; // L2 network name string public networkName; // Current accumulate input hash bytes32 public lastAccInputHash; // Queue of forced batches with their associated data // ForceBatchNum --> hashedForcedBatchData // hashedForcedBatchData: hash containing the necessary information to force a batch: // keccak256(keccak256(bytes transactions), bytes32 forcedGlobalExitRoot, unint64 forcedTimestamp, bytes32 forcedBlockHashL1) mapping(uint64 => bytes32) public forcedBatches; // Last forced batch uint64 public lastForceBatch; // Last forced batch included in the sequence uint64 public lastForceBatchSequenced; // Force batch timeout uint64 public forceBatchTimeout; // Indicates what address is able to do forced batches // If the address is set to 0, forced batches are open to everyone address public forceBatchAddress; // Token address that will be used to pay gas fees in this rollup. This variable it's just for read purposes address public gasTokenAddress; // Native network of the token address of the gas tokena address. This variable it's just for read purposes uint32 public gasTokenNetwork; /** * @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. */ uint256[50] private _gap; /** * @dev Emitted when the trusted sequencer sends a new batch of transactions */ event SequenceBatches(uint64 indexed numBatch, bytes32 l1InfoRoot); /** * @dev Emitted when a batch is forced */ event ForceBatch( uint64 indexed forceBatchNum, bytes32 lastGlobalExitRoot, address sequencer, bytes transactions ); /** * @dev Emitted when forced batches are sequenced by not the trusted sequencer */ event SequenceForceBatches(uint64 indexed numBatch); /** * @dev Emitted when the contract is initialized, contain the first sequenced transaction */ event InitialSequenceBatches( bytes transactions, bytes32 lastGlobalExitRoot, address sequencer ); /** * @dev Emitted when a aggregator verifies batches */ event VerifyBatches( uint64 indexed numBatch, bytes32 stateRoot, address indexed aggregator ); /** * @dev Emitted when the admin updates the trusted sequencer address */ event SetTrustedSequencer(address newTrustedSequencer); /** * @dev Emitted when the admin updates the sequencer URL */ event SetTrustedSequencerURL(string newTrustedSequencerURL); /** * @dev Emitted when the admin update the force batch timeout */ event SetForceBatchTimeout(uint64 newforceBatchTimeout); /** * @dev Emitted when the admin update the force batch address */ event SetForceBatchAddress(address newForceBatchAddress); /** * @dev Emitted when the admin starts the two-step transfer role setting a new pending admin */ event TransferAdminRole(address newPendingAdmin); /** * @dev Emitted when the pending admin accepts the admin role */ event AcceptAdminRole(address newAdmin); // General parameters that will have in common all networks that deploys rollup manager /** * @param _globalExitRootManager Global exit root manager address * @param _pol POL token address * @param _bridgeAddress Bridge address * @param _rollupManager Global exit root manager address */ constructor( IPolygonZkEVMGlobalExitRootV2 _globalExitRootManager, IERC20Upgradeable _pol, IPolygonZkEVMBridgeV2 _bridgeAddress, PolygonRollupManager _rollupManager ) { globalExitRootManager = _globalExitRootManager; pol = _pol; bridgeAddress = _bridgeAddress; rollupManager = _rollupManager; } /** * @param _admin Admin address * @param sequencer Trusted sequencer address * @param networkID Indicates the network identifier that will be used in the bridge * @param _gasTokenAddress Indicates the token address in mainnet that will be used as a gas token * Note if a wrapped token of the bridge is used, the original network and address of this wrapped are used instead * @param sequencerURL Trusted sequencer URL * @param _networkName L2 network name */ function initialize( address _admin, address sequencer, uint32 networkID, address _gasTokenAddress, string memory sequencerURL, string memory _networkName ) external virtual onlyRollupManager initializer { bytes memory gasTokenMetadata; if (_gasTokenAddress != address(0)) { // Ask for token metadata, the same way is enconded in the bridge // Note that this function will revert if the token is not in this network // Note that this could be a possible reentrant call, but cannot make changes on the state since are static call gasTokenMetadata = bridgeAddress.getTokenMetadata(_gasTokenAddress); // Check gas token address on the bridge ( uint32 originWrappedNetwork, address originWrappedAddress ) = bridgeAddress.wrappedTokenToTokenInfo(_gasTokenAddress); if (originWrappedNetwork != 0) { // It's a wrapped token, get the wrapped parameters gasTokenAddress = originWrappedAddress; gasTokenNetwork = originWrappedNetwork; } else { // gasTokenNetwork will be mainnet, for instance 0 gasTokenAddress = _gasTokenAddress; } } // Sequence transaction to initilize the bridge // Calculate transaction to initialize the bridge bytes memory transaction = generateInitializeTransaction( networkID, gasTokenAddress, gasTokenNetwork, gasTokenMetadata ); bytes32 currentTransactionsHash = keccak256(transaction); // Get current timestamp and global exit root uint64 currentTimestamp = uint64(block.timestamp); bytes32 lastGlobalExitRoot = globalExitRootManager .getLastGlobalExitRoot(); // Add the transaction to the sequence as if it was a force transaction bytes32 newAccInputHash = keccak256( abi.encodePacked( bytes32(0), // Current acc Input hash currentTransactionsHash, lastGlobalExitRoot, // Global exit root currentTimestamp, sequencer, blockhash(block.number - 1) ) ); lastAccInputHash = newAccInputHash; rollupManager.onSequenceBatches( uint64(1), // num total batches newAccInputHash ); // Set initialize variables admin = _admin; trustedSequencer = sequencer; trustedSequencerURL = sequencerURL; networkName = _networkName; forceBatchAddress = _admin; // Constant deployment variables forceBatchTimeout = 5 days; emit InitialSequenceBatches(transaction, lastGlobalExitRoot, sequencer); } modifier onlyAdmin() { if (admin != msg.sender) { revert OnlyAdmin(); } _; } modifier onlyTrustedSequencer() { if (trustedSequencer != msg.sender) { revert OnlyTrustedSequencer(); } _; } modifier isSenderAllowedToForceBatches() { address cacheForceBatchAddress = forceBatchAddress; if ( cacheForceBatchAddress != address(0) && cacheForceBatchAddress != msg.sender ) { revert ForceBatchNotAllowed(); } _; } modifier onlyRollupManager() { if (address(rollupManager) != msg.sender) { revert OnlyRollupManager(); } _; } ///////////////////////////////////// // Sequence/Verify batches functions //////////////////////////////////// /** * @notice Allows a sequencer to send multiple batches * @param batches Struct array which holds the necessary data to append new batches to the sequence * @param maxSequenceTimestamp Max timestamp of the sequence. This timestamp must be inside a safety range (actual + 36 seconds). * This timestamp should be equal or higher of the last block inside the sequence, otherwise this batch will be invalidated by circuit. * @param initSequencedBatch This parameter must match the current last batch sequenced. * This will be a protection for the sequencer to avoid sending undesired data * @param l2Coinbase Address that will receive the fees from L2 * note Pol is not a reentrant token */ function sequenceBatches( BatchData[] calldata batches, uint64 maxSequenceTimestamp, uint64 initSequencedBatch, address l2Coinbase ) public virtual onlyTrustedSequencer { uint256 batchesNum = batches.length; if (batchesNum == 0) { revert SequenceZeroBatches(); } if (batchesNum > _MAX_VERIFY_BATCHES) { revert ExceedMaxVerifyBatches(); } // Check max sequence timestamp inside of range if ( uint256(maxSequenceTimestamp) > (block.timestamp + TIMESTAMP_RANGE) ) { revert MaxTimestampSequenceInvalid(); } // Update global exit root if there are new deposits bridgeAddress.updateGlobalExitRoot(); // Get global batch variables bytes32 l1InfoRoot = globalExitRootManager.getRoot(); // Store storage variables in memory, to save gas, because will be overrided multiple times uint64 currentLastForceBatchSequenced = lastForceBatchSequenced; bytes32 currentAccInputHash = lastAccInputHash; // Store in a temporal variable, for avoid access again the storage slot uint64 initLastForceBatchSequenced = currentLastForceBatchSequenced; for (uint256 i = 0; i < batchesNum; i++) { // Load current sequence BatchData memory currentBatch = batches[i]; // Store the current transactions hash since can be used more than once for gas saving bytes32 currentTransactionsHash = keccak256( currentBatch.transactions ); // Check if it's a forced batch if (currentBatch.forcedTimestamp > 0) { currentLastForceBatchSequenced++; // Check forced data matches bytes32 hashedForcedBatchData = keccak256( abi.encodePacked( currentTransactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, currentBatch.forcedBlockHashL1 ) ); if ( hashedForcedBatchData != forcedBatches[currentLastForceBatchSequenced] ) { revert ForcedDataDoesNotMatch(); } // Calculate next accumulated input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentTransactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, l2Coinbase, currentBatch.forcedBlockHashL1 ) ); // Delete forceBatch data since won't be used anymore delete forcedBatches[currentLastForceBatchSequenced]; } else { // Note that forcedGlobalExitRoot and forcedBlockHashL1 remain unused and unchecked in this path // The synchronizer should be aware of that if ( currentBatch.transactions.length > _MAX_TRANSACTIONS_BYTE_LENGTH ) { revert TransactionsLengthAboveMax(); } // Calculate next accumulated input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentTransactionsHash, l1InfoRoot, maxSequenceTimestamp, l2Coinbase, bytes32(0) ) ); } } // Sanity check, should be unreachable if (currentLastForceBatchSequenced > lastForceBatch) { revert ForceBatchesOverflow(); } // Store back the storage variables lastAccInputHash = currentAccInputHash; uint256 nonForcedBatchesSequenced = batchesNum; // Check if there has been forced batches if (currentLastForceBatchSequenced != initLastForceBatchSequenced) { uint64 forcedBatchesSequenced = currentLastForceBatchSequenced - initLastForceBatchSequenced; // substract forced batches nonForcedBatchesSequenced -= forcedBatchesSequenced; // Transfer pol for every forced batch submitted pol.safeTransfer( address(rollupManager), calculatePolPerForceBatch() * (forcedBatchesSequenced) ); // Store new last force batch sequenced lastForceBatchSequenced = currentLastForceBatchSequenced; } // Pay collateral for every non-forced batch submitted pol.safeTransferFrom( msg.sender, address(rollupManager), rollupManager.getBatchFee() * nonForcedBatchesSequenced ); uint64 currentBatchSequenced = rollupManager.onSequenceBatches( uint64(batchesNum), currentAccInputHash ); // Check init sequenced batch if ( initSequencedBatch != (currentBatchSequenced - uint64(batchesNum)) ) { revert InitSequencedBatchDoesNotMatch(); } emit SequenceBatches(currentBatchSequenced, l1InfoRoot); } /** * @notice Callback on verify batches, can only be called by the rollup manager * @param lastVerifiedBatch Last verified batch * @param newStateRoot new state root * @param aggregator Aggregator address */ function onVerifyBatches( uint64 lastVerifiedBatch, bytes32 newStateRoot, address aggregator ) public virtual override onlyRollupManager { emit VerifyBatches(lastVerifiedBatch, newStateRoot, aggregator); } //////////////////////////// // Force batches functions //////////////////////////// /** * @notice Allows a sequencer/user to force a batch of L2 transactions. * This should be used only in extreme cases where the trusted sequencer does not work as expected * Note The sequencer has certain degree of control on how non-forced and forced batches are ordered * In order to assure that users force transactions will be processed properly, user must not sign any other transaction * with the same nonce * @param transactions L2 ethereum transactions EIP-155 or pre-EIP-155 with signature: * @param polAmount Max amount of pol tokens that the sender is willing to pay */ function forceBatch( bytes calldata transactions, uint256 polAmount ) public virtual isSenderAllowedToForceBatches { // Check if rollup manager is on emergency state if (rollupManager.isEmergencyState()) { revert ForceBatchesNotAllowedOnEmergencyState(); } // Calculate pol collateral uint256 polFee = rollupManager.getForcedBatchFee(); if (polFee > polAmount) { revert NotEnoughPOLAmount(); } if (transactions.length > _MAX_FORCE_BATCH_BYTE_LENGTH) { revert TransactionsLengthAboveMax(); } // keep the pol fees on this contract until forced it's sequenced pol.safeTransferFrom(msg.sender, address(this), polFee); // Get globalExitRoot global exit root bytes32 lastGlobalExitRoot = globalExitRootManager .getLastGlobalExitRoot(); // Update forcedBatches mapping lastForceBatch++; forcedBatches[lastForceBatch] = keccak256( abi.encodePacked( keccak256(transactions), lastGlobalExitRoot, uint64(block.timestamp), blockhash(block.number - 1) ) ); if (msg.sender == tx.origin) { // Getting the calldata from an EOA is easy so no need to put the `transactions` in the event emit ForceBatch(lastForceBatch, lastGlobalExitRoot, msg.sender, ""); } else { // Getting internal transaction calldata is complicated (because it requires an archive node) // Therefore it's worth it to put the `transactions` in the event, which is easy to query emit ForceBatch( lastForceBatch, lastGlobalExitRoot, msg.sender, transactions ); } } /** * @notice Allows anyone to sequence forced Batches if the trusted sequencer has not done so in the timeout period * @param batches Struct array which holds the necessary data to append force batches */ function sequenceForceBatches( BatchData[] calldata batches ) external virtual isSenderAllowedToForceBatches { // Check if rollup manager is on emergency state if ( rollupManager.lastDeactivatedEmergencyStateTimestamp() + _HALT_AGGREGATION_TIMEOUT > block.timestamp ) { revert HaltTimeoutNotExpiredAfterEmergencyState(); } uint256 batchesNum = batches.length; if (batchesNum == 0) { revert SequenceZeroBatches(); } if (batchesNum > _MAX_VERIFY_BATCHES) { revert ExceedMaxVerifyBatches(); } if ( uint256(lastForceBatchSequenced) + batchesNum > uint256(lastForceBatch) ) { revert ForceBatchesOverflow(); } // Store storage variables in memory, to save gas, because will be overrided multiple times uint64 currentLastForceBatchSequenced = lastForceBatchSequenced; bytes32 currentAccInputHash = lastAccInputHash; // Sequence force batches for (uint256 i = 0; i < batchesNum; i++) { // Load current sequence BatchData memory currentBatch = batches[i]; currentLastForceBatchSequenced++; // Store the current transactions hash since it's used more than once for gas saving bytes32 currentTransactionsHash = keccak256( currentBatch.transactions ); // Check forced data matches bytes32 hashedForcedBatchData = keccak256( abi.encodePacked( currentTransactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, currentBatch.forcedBlockHashL1 ) ); if ( hashedForcedBatchData != forcedBatches[currentLastForceBatchSequenced] ) { revert ForcedDataDoesNotMatch(); } // Delete forceBatch data since won't be used anymore delete forcedBatches[currentLastForceBatchSequenced]; if (i == (batchesNum - 1)) { // The last batch will have the most restrictive timestamp if ( currentBatch.forcedTimestamp + forceBatchTimeout > block.timestamp ) { revert ForceBatchTimeoutNotExpired(); } } // Calculate next acc input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentTransactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, msg.sender, currentBatch.forcedBlockHashL1 ) ); } // Transfer pol for every forced batch submitted pol.safeTransfer( address(rollupManager), calculatePolPerForceBatch() * (batchesNum) ); // Store back the storage variables lastAccInputHash = currentAccInputHash; lastForceBatchSequenced = currentLastForceBatchSequenced; uint64 currentBatchSequenced = rollupManager.onSequenceBatches( uint64(batchesNum), currentAccInputHash ); emit SequenceForceBatches(currentBatchSequenced); } ////////////////// // admin functions ////////////////// /** * @notice Allow the admin to set a new trusted sequencer * @param newTrustedSequencer Address of the new trusted sequencer */ function setTrustedSequencer( address newTrustedSequencer ) external onlyAdmin { trustedSequencer = newTrustedSequencer; emit SetTrustedSequencer(newTrustedSequencer); } /** * @notice Allow the admin to set the trusted sequencer URL * @param newTrustedSequencerURL URL of trusted sequencer */ function setTrustedSequencerURL( string memory newTrustedSequencerURL ) external onlyAdmin { trustedSequencerURL = newTrustedSequencerURL; emit SetTrustedSequencerURL(newTrustedSequencerURL); } /** * @notice Allow the admin to change the force batch address, that will be allowed to force batches * If address 0 is set, then everyone is able to force batches, this action is irreversible * @param newForceBatchAddress New force batch address */ function setForceBatchAddress( address newForceBatchAddress ) external onlyAdmin { if (forceBatchAddress == address(0)) { revert ForceBatchesDecentralized(); } forceBatchAddress = newForceBatchAddress; emit SetForceBatchAddress(newForceBatchAddress); } /** * @notice Allow the admin to set the forcedBatchTimeout * The new value can only be lower, except if emergency state is active * @param newforceBatchTimeout New force batch timeout */ function setForceBatchTimeout( uint64 newforceBatchTimeout ) external onlyAdmin { if (newforceBatchTimeout > _HALT_AGGREGATION_TIMEOUT) { revert InvalidRangeForceBatchTimeout(); } if (!rollupManager.isEmergencyState()) { if (newforceBatchTimeout >= forceBatchTimeout) { revert InvalidRangeForceBatchTimeout(); } } forceBatchTimeout = newforceBatchTimeout; emit SetForceBatchTimeout(newforceBatchTimeout); } /** * @notice Starts the admin role transfer * This is a two step process, the pending admin must accepted to finalize the process * @param newPendingAdmin Address of the new pending admin */ function transferAdminRole(address newPendingAdmin) external onlyAdmin { pendingAdmin = newPendingAdmin; emit TransferAdminRole(newPendingAdmin); } /** * @notice Allow the current pending admin to accept the admin role */ function acceptAdminRole() external { if (pendingAdmin != msg.sender) { revert OnlyPendingAdmin(); } admin = pendingAdmin; emit AcceptAdminRole(pendingAdmin); } ////////////////// // view/pure functions ////////////////// /** * @notice Function to calculate the reward for a forced batch */ function calculatePolPerForceBatch() public view returns (uint256) { uint256 currentBalance = pol.balanceOf(address(this)); // Pending forced Batches = last forced batch added - last forced batch sequenced uint256 pendingForcedBatches = lastForceBatch - lastForceBatchSequenced; if (pendingForcedBatches == 0) return 0; return currentBalance / pendingForcedBatches; } /** * @notice Generate Initialize transaction for hte bridge on L2 * @param networkID Indicates the network identifier that will be used in the bridge * @param _gasTokenAddress Indicates the token address that will be used to pay gas fees in the new rollup * @param _gasTokenNetwork Indicates the native network of the token address * @param _gasTokenMetadata Abi encoded gas token metadata */ function generateInitializeTransaction( uint32 networkID, address _gasTokenAddress, uint32 _gasTokenNetwork, bytes memory _gasTokenMetadata ) public view returns (bytes memory) { bytes memory initializeBrigeData = abi.encodeCall( IPolygonZkEVMBridgeV2.initialize, ( networkID, _gasTokenAddress, _gasTokenNetwork, GLOBAL_EXIT_ROOT_MANAGER_L2, address(0), // Rollup manager on L2 does not exist _gasTokenMetadata ) ); bytes memory bytesToSign; if (_gasTokenMetadata.length == 0) { bytesToSign = abi.encodePacked( INITIALIZE_TX_BRIDGE_LIST_LEN_LEN, uint16(initializeBrigeData.length) + INITIALIZE_TX_CONSTANT_BYTES_EMPTY_METADATA, // do not support more than 2 bytes of length, intended to revert on overflow INITIALIZE_TX_BRIDGE_PARAMS, bridgeAddress, INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS_EMPTY_METADATA, INITIALIZE_TX_DATA_LEN_EMPTY_METADATA, initializeBrigeData ); } else { // Do not support more than 65535 bytes if (initializeBrigeData.length > type(uint16).max) { revert HugeTokenMetadataNotSupported(); } uint16 initializeBrigeDataLen = uint16(initializeBrigeData.length); bytesToSign = abi.encodePacked( INITIALIZE_TX_BRIDGE_LIST_LEN_LEN, uint16(initializeBrigeData.length) + INITIALIZE_TX_CONSTANT_BYTES, // do not support more than 2 bytes of length, intended to revert on overflow INITIALIZE_TX_BRIDGE_PARAMS, bridgeAddress, INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS, initializeBrigeDataLen, initializeBrigeData ); } // Sanity check that the ecrecover will work // Should never happen that giving a valid signature, ecrecover "breaks" address signer = ecrecover( keccak256(bytesToSign), SIGNATURE_INITIALIZE_TX_V, SIGNATURE_INITIALIZE_TX_R, SIGNATURE_INITIALIZE_TX_S ); if (signer == address(0)) { revert InvalidInitializeTransaction(); } bytes memory transaction = abi.encodePacked( bytesToSign, SIGNATURE_INITIALIZE_TX_R, SIGNATURE_INITIALIZE_TX_S, SIGNATURE_INITIALIZE_TX_V, INITIALIZE_TX_EFFECTIVE_PERCENTAGE ); return transaction; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/TransparentUpgradeableProxy.sol) pragma solidity ^0.8.20; import {ERC1967Utils} from "@openzeppelin/contracts5/proxy/ERC1967/ERC1967Utils.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts5/proxy/ERC1967/ERC1967Proxy.sol"; import {IERC1967} from "@openzeppelin/contracts5/interfaces/IERC1967.sol"; import {ProxyAdmin} from "@openzeppelin/contracts5/proxy/transparent/ProxyAdmin.sol"; import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts5/proxy/transparent/TransparentUpgradeableProxy.sol"; /** * @dev Contrac TransparentUpgradeableProxy from Openzeppelin v5 with the following modifications: * - Admin is a parameter in the constructor ( like previous versions) isntead of being deployed * - Let the admin get access to the proxy * - Replace _msgSender() with msg.sender */ contract PolygonTransparentProxy is ERC1967Proxy { // An immutable address for the admin to avoid unnecessary SLOADs before each call // at the expense of removing the ability to change the admin once it's set. // This is acceptable if the admin is always a ProxyAdmin instance or similar contract // with its own ability to transfer the permissions to another account. address private immutable _admin; /** * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`, * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in * {ERC1967Proxy-constructor}. */ constructor( address _logic, address admin, bytes memory _data ) payable ERC1967Proxy(_logic, _data) { _admin = admin; // Set the storage value and emit an event for ERC-1967 compatibility ERC1967Utils.changeAdmin(_proxyAdmin()); } /** * @dev Returns the admin of this proxy. */ function _proxyAdmin() internal virtual returns (address) { return _admin; } /** * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior. */ function _fallback() internal virtual override { if (msg.sender == _proxyAdmin()) { if ( msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector ) { super._fallback(); } else { _dispatchUpgradeToAndCall(); } } else { super._fallback(); } } /** * @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}. * * Requirements: * * - If `data` is empty, `msg.value` must be zero. */ function _dispatchUpgradeToAndCall() private { (address newImplementation, bytes memory data) = abi.decode( msg.data[4:], (address, bytes) ); ERC1967Utils.upgradeToAndCall(newImplementation, data); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.20; import "./interfaces/IPolygonRollupManager.sol"; import "./interfaces/IPolygonZkEVMGlobalExitRootV2.sol"; import "../interfaces/IPolygonZkEVMBridge.sol"; import "./interfaces/IPolygonRollupBase.sol"; import "../interfaces/IVerifierRollup.sol"; import "../lib/EmergencyManager.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "./lib/PolygonTransparentProxy.sol"; import "./lib/PolygonAccessControlUpgradeable.sol"; import "./lib/LegacyZKEVMStateVariables.sol"; import "./consensus/zkEVM/PolygonZkEVMExistentEtrog.sol"; import "./lib/PolygonConstantsBase.sol"; /** * Contract responsible for managing rollups and the verification of their batches. * This contract will create and update rollups and store all the hashed sequenced data from them. * The logic for sequence batches is moved to the `consensus` contracts, while the verification of all of * them will be done in this one. In this way, the proof aggregation of the rollups will be easier on a close future. */ contract PolygonRollupManager is PolygonAccessControlUpgradeable, EmergencyManager, LegacyZKEVMStateVariables, PolygonConstantsBase, IPolygonRollupManager { using SafeERC20Upgradeable for IERC20Upgradeable; /** * @notice Struct which to store the rollup type data * @param consensusImplementation Consensus implementation ( contains the consensus logic for the transaparent proxy) * @param verifier verifier * @param forkID fork ID * @param rollupCompatibilityID Rollup compatibility ID, to check upgradability between rollup types * @param obsolete Indicates if the rollup type is obsolete * @param genesis Genesis block of the rollup, note that will only be used on creating new rollups, not upgrade them */ struct RollupType { address consensusImplementation; IVerifierRollup verifier; uint64 forkID; uint8 rollupCompatibilityID; bool obsolete; bytes32 genesis; } /** * @notice Struct which to store the rollup data of each chain * @param rollupContract Rollup consensus contract, which manages everything * related to sequencing transactions * @param chainID Chain ID of the rollup * @param verifier Verifier contract * @param forkID ForkID of the rollup * @param batchNumToStateRoot State root mapping * @param sequencedBatches Queue of batches that defines the virtual state * @param pendingStateTransitions Pending state mapping * @param lastLocalExitRoot Last exit root verified, used for compute the rollupExitRoot * @param lastBatchSequenced Last batch sent by the consensus contract * @param lastVerifiedBatch Last batch verified * @param lastPendingState Last pending state * @param lastPendingStateConsolidated Last pending state consolidated * @param lastVerifiedBatchBeforeUpgrade Last batch verified before the last upgrade * @param rollupTypeID Rollup type ID, can be 0 if it was added as an existing rollup * @param rollupCompatibilityID Rollup ID used for compatibility checks when upgrading */ struct RollupData { IPolygonRollupBase rollupContract; uint64 chainID; IVerifierRollup verifier; uint64 forkID; mapping(uint64 batchNum => bytes32) batchNumToStateRoot; mapping(uint64 batchNum => SequencedBatchData) sequencedBatches; mapping(uint256 pendingStateNum => PendingState) pendingStateTransitions; bytes32 lastLocalExitRoot; uint64 lastBatchSequenced; uint64 lastVerifiedBatch; uint64 lastPendingState; uint64 lastPendingStateConsolidated; uint64 lastVerifiedBatchBeforeUpgrade; uint64 rollupTypeID; uint8 rollupCompatibilityID; } // Modulus zkSNARK uint256 internal constant _RFIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Max batch multiplier per verification uint256 internal constant _MAX_BATCH_MULTIPLIER = 12; // Max batch fee value uint256 internal constant _MAX_BATCH_FEE = 1000 ether; // Min value batch fee uint256 internal constant _MIN_BATCH_FEE = 1 gwei; // Goldilocks prime field uint256 internal constant _GOLDILOCKS_PRIME_FIELD = 0xFFFFFFFF00000001; // 2 ** 64 - 2 ** 32 + 1 // Max uint64 uint256 internal constant _MAX_UINT_64 = type(uint64).max; // 0xFFFFFFFFFFFFFFFF // Exit merkle tree levels uint256 internal constant _EXIT_TREE_DEPTH = 32; // Roles // Be able to add a new rollup type bytes32 internal constant _ADD_ROLLUP_TYPE_ROLE = keccak256("ADD_ROLLUP_TYPE_ROLE"); // Be able to obsolete a rollup type, which means that new rollups cannot use this type bytes32 internal constant _OBSOLETE_ROLLUP_TYPE_ROLE = keccak256("OBSOLETE_ROLLUP_TYPE_ROLE"); // Be able to create a new rollup using a rollup type bytes32 internal constant _CREATE_ROLLUP_ROLE = keccak256("CREATE_ROLLUP_ROLE"); // Be able to create a new rollup which does not have to follow any rollup type. // Also sets the genesis block for that network bytes32 internal constant _ADD_EXISTING_ROLLUP_ROLE = keccak256("ADD_EXISTING_ROLLUP_ROLE"); // Be able to update a rollup to a new rollup type that it's compatible bytes32 internal constant _UPDATE_ROLLUP_ROLE = keccak256("UPDATE_ROLLUP_ROLE"); // Be able to that has priority to verify batches and consolidates the state instantly bytes32 internal constant _TRUSTED_AGGREGATOR_ROLE = keccak256("TRUSTED_AGGREGATOR_ROLE"); // Be able to set the trusted aggregator address bytes32 internal constant _TRUSTED_AGGREGATOR_ROLE_ADMIN = keccak256("TRUSTED_AGGREGATOR_ROLE_ADMIN"); // Be able to tweak parameters bytes32 internal constant _TWEAK_PARAMETERS_ROLE = keccak256("TWEAK_PARAMETERS_ROLE"); // Be able to set the current batch fee bytes32 internal constant _SET_FEE_ROLE = keccak256("SET_FEE_ROLE"); // Be able to stop the emergency state bytes32 internal constant _STOP_EMERGENCY_ROLE = keccak256("STOP_EMERGENCY_ROLE"); // Be able to activate the emergency state without any further condition bytes32 internal constant _EMERGENCY_COUNCIL_ROLE = keccak256("EMERGENCY_COUNCIL_ROLE"); // Be able to set the emergency council address bytes32 internal constant _EMERGENCY_COUNCIL_ADMIN = keccak256("EMERGENCY_COUNCIL_ADMIN"); // Global Exit Root address IPolygonZkEVMGlobalExitRootV2 public immutable globalExitRootManager; // PolygonZkEVM Bridge Address IPolygonZkEVMBridge public immutable bridgeAddress; // POL token address IERC20Upgradeable public immutable pol; // Number of rollup types added, every new type will be assigned sequencially a new ID uint32 public rollupTypeCount; // Rollup type mapping mapping(uint32 rollupTypeID => RollupType) public rollupTypeMap; // Number of rollups added, every new rollup will be assigned sequencially a new ID uint32 public rollupCount; // Rollups ID mapping mapping(uint32 rollupID => RollupData) public rollupIDToRollupData; // Rollups address mapping mapping(address rollupAddress => uint32 rollupID) public rollupAddressToID; // Chain ID mapping for nullifying // note we will take care to avoid that current known chainIDs are not reused in our networks (example: 1) mapping(uint64 chainID => uint32 rollupID) public chainIDToRollupID; // Total sequenced batches across all rollups uint64 public totalSequencedBatches; // Total verified batches across all rollups uint64 public totalVerifiedBatches; // Last timestamp when an aggregation happen uint64 public lastAggregationTimestamp; // Trusted aggregator timeout, if a sequence is not verified in this time frame, // everyone can verify that sequence uint64 public trustedAggregatorTimeout; // Once a pending state exceeds this timeout it can be consolidated uint64 public pendingStateTimeout; // Time target of the verification of a batch // Adaptively the batchFee will be updated to achieve this target uint64 public verifyBatchTimeTarget; // Batch fee multiplier with 3 decimals that goes from 1000 - 1023 uint16 public multiplierBatchFee; // Current POL fee per batch sequenced // note This variable is internal, since the view function getBatchFee is likely to be upgraded uint256 internal _batchFee; // Timestamp when the last emergency state was deactivated uint64 public lastDeactivatedEmergencyStateTimestamp; /** * @dev Emitted when a new rollup type is added */ event AddNewRollupType( uint32 indexed rollupTypeID, address consensusImplementation, address verifier, uint64 forkID, uint8 rollupCompatibilityID, bytes32 genesis, string description ); /** * @dev Emitted when a a rolup type is obsoleted */ event ObsoleteRollupType(uint32 indexed rollupTypeID); /** * @dev Emitted when a new rollup is created based on a rollupType */ event CreateNewRollup( uint32 indexed rollupID, uint32 rollupTypeID, address rollupAddress, uint64 chainID, address gasTokenAddress ); /** * @dev Emitted when an existing rollup is added */ event AddExistingRollup( uint32 indexed rollupID, uint64 forkID, address rollupAddress, uint64 chainID, uint8 rollupCompatibilityID, uint64 lastVerifiedBatchBeforeUpgrade ); /** * @dev Emitted when a rollup is udpated */ event UpdateRollup( uint32 indexed rollupID, uint32 newRollupTypeID, uint64 lastVerifiedBatchBeforeUpgrade ); /** * @dev Emitted when a new verifier is added */ event OnSequenceBatches(uint32 indexed rollupID, uint64 lastBatchSequenced); /** * @dev Emitted when an aggregator verifies batches */ event VerifyBatches( uint32 indexed rollupID, uint64 numBatch, bytes32 stateRoot, bytes32 exitRoot, address indexed aggregator ); /** * @dev Emitted when the trusted aggregator verifies batches */ event VerifyBatchesTrustedAggregator( uint32 indexed rollupID, uint64 numBatch, bytes32 stateRoot, bytes32 exitRoot, address indexed aggregator ); /** * @dev Emitted when pending state is consolidated */ event ConsolidatePendingState( uint32 indexed rollupID, uint64 numBatch, bytes32 stateRoot, bytes32 exitRoot, uint64 pendingStateNum ); /** * @dev Emitted when is proved a different state given the same batches */ event ProveNonDeterministicPendingState( bytes32 storedStateRoot, bytes32 provedStateRoot ); /** * @dev Emitted when the trusted aggregator overrides pending state */ event OverridePendingState( uint32 indexed rollupID, uint64 numBatch, bytes32 stateRoot, bytes32 exitRoot, address aggregator ); /** * @dev Emitted when is updated the trusted aggregator timeout */ event SetTrustedAggregatorTimeout(uint64 newTrustedAggregatorTimeout); /** * @dev Emitted when is updated the pending state timeout */ event SetPendingStateTimeout(uint64 newPendingStateTimeout); /** * @dev Emitted when is updated the multiplier batch fee */ event SetMultiplierBatchFee(uint16 newMultiplierBatchFee); /** * @dev Emitted when is updated the verify batch timeout */ event SetVerifyBatchTimeTarget(uint64 newVerifyBatchTimeTarget); /** * @dev Emitted when is updated the trusted aggregator address */ event SetTrustedAggregator(address newTrustedAggregator); /** * @dev Emitted when is updated the batch fee */ event SetBatchFee(uint256 newBatchFee); /** * @param _globalExitRootManager Global exit root manager address * @param _pol POL token address * @param _bridgeAddress Bridge address */ constructor( IPolygonZkEVMGlobalExitRootV2 _globalExitRootManager, IERC20Upgradeable _pol, IPolygonZkEVMBridge _bridgeAddress ) { globalExitRootManager = _globalExitRootManager; pol = _pol; bridgeAddress = _bridgeAddress; // Disable initalizers on the implementation following the best practices _disableInitializers(); } /** * @param trustedAggregator Trusted aggregator address * @param _pendingStateTimeout Pending state timeout * @param _trustedAggregatorTimeout Trusted aggregator timeout * @param admin Admin of the rollup manager * @param timelock Timelock address * @param emergencyCouncil Emergency council address * @param polygonZkEVM New deployed Polygon zkEVM which will be initialized wiht previous values * @param zkEVMVerifier Verifier of the new zkEVM deployed * @param zkEVMForkID Fork id of the new zkEVM deployed * @param zkEVMChainID Chain id of the new zkEVM deployed */ function initialize( address trustedAggregator, uint64 _pendingStateTimeout, uint64 _trustedAggregatorTimeout, address admin, address timelock, address emergencyCouncil, PolygonZkEVMExistentEtrog polygonZkEVM, IVerifierRollup zkEVMVerifier, uint64 zkEVMForkID, uint64 zkEVMChainID ) external virtual reinitializer(2) { pendingStateTimeout = _pendingStateTimeout; trustedAggregatorTimeout = _trustedAggregatorTimeout; // Constant deployment variables _batchFee = 0.1 ether; // 0.1 POL verifyBatchTimeTarget = 30 minutes; multiplierBatchFee = 1002; // Initialize OZ contracts __AccessControl_init(); // setup roles // trusted aggregator role _setupRole(_TRUSTED_AGGREGATOR_ROLE, trustedAggregator); // Timelock roles _setupRole(DEFAULT_ADMIN_ROLE, timelock); _setupRole(_ADD_ROLLUP_TYPE_ROLE, timelock); _setupRole(_ADD_EXISTING_ROLLUP_ROLE, timelock); // note even this role can only update to an already added verifier/consensus // Could break the compatibility of them, changing the virtual state _setupRole(_UPDATE_ROLLUP_ROLE, timelock); // admin roles _setupRole(_OBSOLETE_ROLLUP_TYPE_ROLE, admin); _setupRole(_CREATE_ROLLUP_ROLE, admin); _setupRole(_STOP_EMERGENCY_ROLE, admin); _setupRole(_TWEAK_PARAMETERS_ROLE, admin); // admin should be able to update the trusted aggregator address _setRoleAdmin(_TRUSTED_AGGREGATOR_ROLE, _TRUSTED_AGGREGATOR_ROLE_ADMIN); _setupRole(_TRUSTED_AGGREGATOR_ROLE_ADMIN, admin); _setupRole(_SET_FEE_ROLE, admin); // Emergency council roles _setRoleAdmin(_EMERGENCY_COUNCIL_ROLE, _EMERGENCY_COUNCIL_ADMIN); _setupRole(_EMERGENCY_COUNCIL_ROLE, emergencyCouncil); _setupRole(_EMERGENCY_COUNCIL_ADMIN, emergencyCouncil); // Check last verified batch uint64 zkEVMLastBatchSequenced = _legacylastBatchSequenced; uint64 zkEVMLastVerifiedBatch = _legacyLastVerifiedBatch; if (zkEVMLastBatchSequenced != zkEVMLastVerifiedBatch) { revert AllzkEVMSequencedBatchesMustBeVerified(); } // Initialize current zkEVM RollupData storage currentZkEVM = _addExistingRollup( IPolygonRollupBase(polygonZkEVM), zkEVMVerifier, zkEVMForkID, zkEVMChainID, 0, // Rollup compatibility ID is 0 _legacyLastVerifiedBatch ); // Copy variables from legacy currentZkEVM.batchNumToStateRoot[ zkEVMLastVerifiedBatch ] = _legacyBatchNumToStateRoot[zkEVMLastVerifiedBatch]; // note previousLastBatchSequenced of the SequencedBatchData will be inconsistent, // since there will not be a previous sequence stored in the sequence mapping. // However since lastVerifiedBatch is equal to the lastBatchSequenced // won't affect in any case currentZkEVM.sequencedBatches[ zkEVMLastBatchSequenced ] = _legacySequencedBatches[zkEVMLastBatchSequenced]; currentZkEVM.lastBatchSequenced = zkEVMLastBatchSequenced; currentZkEVM.lastVerifiedBatch = zkEVMLastVerifiedBatch; currentZkEVM.lastVerifiedBatchBeforeUpgrade = zkEVMLastVerifiedBatch; // rollupType and rollupCompatibilityID will be both 0 // Initialize polygon zkevm polygonZkEVM.initializeUpgrade( _legacyAdmin, _legacyTrustedSequencer, _legacyTrustedSequencerURL, _legacyNetworkName, _legacySequencedBatches[zkEVMLastBatchSequenced].accInputHash ); } /////////////////////////////////////// // Rollups management functions /////////////////////////////////////// /** * @notice Add a new rollup type * @param consensusImplementation Consensus implementation * @param verifier Verifier address * @param forkID ForkID of the verifier * @param genesis Genesis block of the rollup * @param description Description of the rollup type */ function addNewRollupType( address consensusImplementation, IVerifierRollup verifier, uint64 forkID, uint8 rollupCompatibilityID, bytes32 genesis, string memory description ) external onlyRole(_ADD_ROLLUP_TYPE_ROLE) { uint32 rollupTypeID = ++rollupTypeCount; rollupTypeMap[rollupTypeID] = RollupType({ consensusImplementation: consensusImplementation, verifier: verifier, forkID: forkID, rollupCompatibilityID: rollupCompatibilityID, obsolete: false, genesis: genesis }); emit AddNewRollupType( rollupTypeID, consensusImplementation, address(verifier), forkID, rollupCompatibilityID, genesis, description ); } /** * @notice Obsolete Rollup type * @param rollupTypeID Rollup type to obsolete */ function obsoleteRollupType( uint32 rollupTypeID ) external onlyRole(_OBSOLETE_ROLLUP_TYPE_ROLE) { // Check that rollup type exists if (rollupTypeID == 0 || rollupTypeID > rollupTypeCount) { revert RollupTypeDoesNotExist(); } // Check rollup type is not obsolete RollupType storage currentRollupType = rollupTypeMap[rollupTypeID]; if (currentRollupType.obsolete == true) { revert RollupTypeObsolete(); } currentRollupType.obsolete = true; emit ObsoleteRollupType(rollupTypeID); } /** * @notice Create a new rollup * @param rollupTypeID Rollup type to deploy * @param chainID ChainID of the rollup, must be a new one * @param admin Admin of the new created rollup * @param sequencer Sequencer of the new created rollup * @param gasTokenAddress Indicates the token address that will be used to pay gas fees in the new rollup * Note if a wrapped token of the bridge is used, the original network and address of this wrapped will be used instead * @param sequencerURL Sequencer URL of the new created rollup * @param networkName Network name of the new created rollup */ function createNewRollup( uint32 rollupTypeID, uint64 chainID, address admin, address sequencer, address gasTokenAddress, string memory sequencerURL, string memory networkName ) external onlyRole(_CREATE_ROLLUP_ROLE) { // Check that rollup type exists if (rollupTypeID == 0 || rollupTypeID > rollupTypeCount) { revert RollupTypeDoesNotExist(); } // Check rollup type is not obsolete RollupType storage rollupType = rollupTypeMap[rollupTypeID]; if (rollupType.obsolete == true) { revert RollupTypeObsolete(); } // Check chainID nullifier if (chainIDToRollupID[chainID] != 0) { revert ChainIDAlreadyExist(); } // Create a new Rollup, using a transparent proxy pattern // Consensus will be the implementation, and this contract the admin uint32 rollupID = ++rollupCount; address rollupAddress = address( new PolygonTransparentProxy( rollupType.consensusImplementation, address(this), new bytes(0) ) ); // Set chainID nullifier chainIDToRollupID[chainID] = rollupID; // Store rollup data rollupAddressToID[rollupAddress] = rollupID; RollupData storage rollup = rollupIDToRollupData[rollupID]; rollup.rollupContract = IPolygonRollupBase(rollupAddress); rollup.forkID = rollupType.forkID; rollup.verifier = rollupType.verifier; rollup.chainID = chainID; rollup.batchNumToStateRoot[0] = rollupType.genesis; rollup.rollupTypeID = rollupTypeID; rollup.rollupCompatibilityID = rollupType.rollupCompatibilityID; emit CreateNewRollup( rollupID, rollupTypeID, rollupAddress, chainID, gasTokenAddress ); // Initialize new rollup IPolygonRollupBase(rollupAddress).initialize( admin, sequencer, rollupID, gasTokenAddress, sequencerURL, networkName ); } /** * @notice Add an already deployed rollup * note that this rollup does not follow any rollupType * @param rollupAddress Rollup address * @param verifier Verifier address, must be added before * @param forkID Fork id of the added rollup * @param chainID Chain id of the added rollup * @param genesis Genesis block for this rollup * @param rollupCompatibilityID Compatibility ID for the added rollup */ function addExistingRollup( IPolygonRollupBase rollupAddress, IVerifierRollup verifier, uint64 forkID, uint64 chainID, bytes32 genesis, uint8 rollupCompatibilityID ) external onlyRole(_ADD_EXISTING_ROLLUP_ROLE) { // Check chainID nullifier if (chainIDToRollupID[chainID] != 0) { revert ChainIDAlreadyExist(); } // Check if rollup address was already added if (rollupAddressToID[address(rollupAddress)] != 0) { revert RollupAddressAlreadyExist(); } RollupData storage rollup = _addExistingRollup( rollupAddress, verifier, forkID, chainID, rollupCompatibilityID, 0 // last verified batch it's always 0 ); rollup.batchNumToStateRoot[0] = genesis; } /** * @notice Add an already deployed rollup * note that this rollup does not follow any rollupType * @param rollupAddress Rollup address * @param verifier Verifier address, must be added before * @param forkID Fork id of the added rollup * @param chainID Chain id of the added rollup * @param rollupCompatibilityID Compatibility ID for the added rollup * @param lastVerifiedBatch Last verified batch before adding the rollup */ function _addExistingRollup( IPolygonRollupBase rollupAddress, IVerifierRollup verifier, uint64 forkID, uint64 chainID, uint8 rollupCompatibilityID, uint64 lastVerifiedBatch ) internal returns (RollupData storage rollup) { uint32 rollupID = ++rollupCount; // Set chainID nullifier chainIDToRollupID[chainID] = rollupID; // Store rollup data rollupAddressToID[address(rollupAddress)] = rollupID; rollup = rollupIDToRollupData[rollupID]; rollup.rollupContract = rollupAddress; rollup.forkID = forkID; rollup.verifier = verifier; rollup.chainID = chainID; rollup.rollupCompatibilityID = rollupCompatibilityID; // rollup type is 0, since it does not follow any rollup type emit AddExistingRollup( rollupID, forkID, address(rollupAddress), chainID, rollupCompatibilityID, lastVerifiedBatch ); } /** * @notice Upgrade an existing rollup * @param rollupContract Rollup consensus proxy address * @param newRollupTypeID New rolluptypeID to upgrade to * @param upgradeData Upgrade data */ function updateRollup( ITransparentUpgradeableProxy rollupContract, uint32 newRollupTypeID, bytes calldata upgradeData ) external onlyRole(_UPDATE_ROLLUP_ROLE) { // Check that rollup type exists if (newRollupTypeID == 0 || newRollupTypeID > rollupTypeCount) { revert RollupTypeDoesNotExist(); } // Check the rollup exists uint32 rollupID = rollupAddressToID[address(rollupContract)]; if (rollupID == 0) { revert RollupMustExist(); } RollupData storage rollup = rollupIDToRollupData[rollupID]; // The update must be to a new rollup type if (rollup.rollupTypeID == newRollupTypeID) { revert UpdateToSameRollupTypeID(); } RollupType storage newRollupType = rollupTypeMap[newRollupTypeID]; // Check rollup type is not obsolete if (newRollupType.obsolete == true) { revert RollupTypeObsolete(); } // Check compatibility of the rollups if ( rollup.rollupCompatibilityID != newRollupType.rollupCompatibilityID ) { revert UpdateNotCompatible(); } // Update rollup parameters rollup.verifier = newRollupType.verifier; rollup.forkID = newRollupType.forkID; rollup.rollupTypeID = newRollupTypeID; uint64 lastVerifiedBatch = getLastVerifiedBatch(rollupID); rollup.lastVerifiedBatchBeforeUpgrade = lastVerifiedBatch; // Upgrade rollup rollupContract.upgradeToAndCall( newRollupType.consensusImplementation, upgradeData ); emit UpdateRollup(rollupID, newRollupTypeID, lastVerifiedBatch); } ///////////////////////////////////// // Sequence/Verify batches functions //////////////////////////////////// /** * @notice Sequence batches, callback called by one of the consensus managed by this contract * @param newSequencedBatches Number of batches sequenced * @param newAccInputHash New accumulate input hash */ function onSequenceBatches( uint64 newSequencedBatches, bytes32 newAccInputHash ) external ifNotEmergencyState returns (uint64) { // Check that the msg.sender is an added rollup uint32 rollupID = rollupAddressToID[msg.sender]; if (rollupID == 0) { revert SenderMustBeRollup(); } // This prevents overwritting sequencedBatches if (newSequencedBatches == 0) { revert MustSequenceSomeBatch(); } RollupData storage rollup = rollupIDToRollupData[rollupID]; // Update total sequence parameters totalSequencedBatches += newSequencedBatches; // Update sequenced batches of the current rollup uint64 previousLastBatchSequenced = rollup.lastBatchSequenced; uint64 newLastBatchSequenced = previousLastBatchSequenced + newSequencedBatches; rollup.lastBatchSequenced = newLastBatchSequenced; rollup.sequencedBatches[newLastBatchSequenced] = SequencedBatchData({ accInputHash: newAccInputHash, sequencedTimestamp: uint64(block.timestamp), previousLastBatchSequenced: previousLastBatchSequenced }); // Consolidate pending state if possible _tryConsolidatePendingState(rollup); emit OnSequenceBatches(rollupID, newLastBatchSequenced); return newLastBatchSequenced; } /** * @notice Allows an aggregator to verify multiple batches * @param rollupID Rollup identifier * @param pendingStateNum Init pending state, 0 if consolidated state is used * @param initNumBatch Batch which the aggregator starts the verification * @param finalNewBatch Last batch aggregator intends to verify * @param newLocalExitRoot New local exit root once the batch is processed * @param newStateRoot New State root once the batch is processed * @param beneficiary Address that will receive the verification reward * @param proof Fflonk proof */ function verifyBatches( uint32 rollupID, uint64 pendingStateNum, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 newStateRoot, address beneficiary, bytes32[24] calldata proof ) external ifNotEmergencyState { RollupData storage rollup = rollupIDToRollupData[rollupID]; // Check if the trusted aggregator timeout expired, // Note that the sequencedBatches struct must exists for this finalNewBatch, if not newAccInputHash will be 0 if ( rollup.sequencedBatches[finalNewBatch].sequencedTimestamp + trustedAggregatorTimeout > block.timestamp ) { revert TrustedAggregatorTimeoutNotExpired(); } if (finalNewBatch - initNumBatch > _MAX_VERIFY_BATCHES) { revert ExceedMaxVerifyBatches(); } _verifyAndRewardBatches( rollup, pendingStateNum, initNumBatch, finalNewBatch, newLocalExitRoot, newStateRoot, beneficiary, proof ); // Update batch fees _updateBatchFee(rollup, finalNewBatch); if (pendingStateTimeout == 0) { // Consolidate state rollup.lastVerifiedBatch = finalNewBatch; rollup.batchNumToStateRoot[finalNewBatch] = newStateRoot; rollup.lastLocalExitRoot = newLocalExitRoot; // Clean pending state if any if (rollup.lastPendingState > 0) { rollup.lastPendingState = 0; rollup.lastPendingStateConsolidated = 0; } // Interact with globalExitRootManager globalExitRootManager.updateExitRoot(getRollupExitRoot()); } else { // Consolidate pending state if possible _tryConsolidatePendingState(rollup); // Update pending state rollup.lastPendingState++; rollup.pendingStateTransitions[ rollup.lastPendingState ] = PendingState({ timestamp: uint64(block.timestamp), lastVerifiedBatch: finalNewBatch, exitRoot: newLocalExitRoot, stateRoot: newStateRoot }); } emit VerifyBatches( rollupID, finalNewBatch, newStateRoot, newLocalExitRoot, msg.sender ); } /** * @notice Allows a trusted aggregator to verify multiple batches * @param rollupID Rollup identifier * @param pendingStateNum Init pending state, 0 if consolidated state is used * @param initNumBatch Batch which the aggregator starts the verification * @param finalNewBatch Last batch aggregator intends to verify * @param newLocalExitRoot New local exit root once the batch is processed * @param newStateRoot New State root once the batch is processed * @param beneficiary Address that will receive the verification reward * @param proof Fflonk proof */ function verifyBatchesTrustedAggregator( uint32 rollupID, uint64 pendingStateNum, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 newStateRoot, address beneficiary, bytes32[24] calldata proof ) external onlyRole(_TRUSTED_AGGREGATOR_ROLE) { RollupData storage rollup = rollupIDToRollupData[rollupID]; _verifyAndRewardBatches( rollup, pendingStateNum, initNumBatch, finalNewBatch, newLocalExitRoot, newStateRoot, beneficiary, proof ); // Consolidate state rollup.lastVerifiedBatch = finalNewBatch; rollup.batchNumToStateRoot[finalNewBatch] = newStateRoot; rollup.lastLocalExitRoot = newLocalExitRoot; // Clean pending state if any if (rollup.lastPendingState > 0) { rollup.lastPendingState = 0; rollup.lastPendingStateConsolidated = 0; } // Interact with globalExitRootManager globalExitRootManager.updateExitRoot(getRollupExitRoot()); emit VerifyBatchesTrustedAggregator( rollupID, finalNewBatch, newStateRoot, newLocalExitRoot, msg.sender ); } /** * @notice Verify and reward batches internal function * @param rollup Rollup Data storage pointer that will be used to the verification * @param pendingStateNum Init pending state, 0 if consolidated state is used * @param initNumBatch Batch which the aggregator starts the verification * @param finalNewBatch Last batch aggregator intends to verify * @param newLocalExitRoot New local exit root once the batch is processed * @param newStateRoot New State root once the batch is processed * @param beneficiary Address that will receive the verification reward * @param proof Fflonk proof */ function _verifyAndRewardBatches( RollupData storage rollup, uint64 pendingStateNum, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 newStateRoot, address beneficiary, bytes32[24] calldata proof ) internal virtual { bytes32 oldStateRoot; uint64 currentLastVerifiedBatch = _getLastVerifiedBatch(rollup); if (initNumBatch < rollup.lastVerifiedBatchBeforeUpgrade) { revert InitBatchMustMatchCurrentForkID(); } // Use pending state if specified, otherwise use consolidated state if (pendingStateNum != 0) { // Check that pending state exist // Already consolidated pending states can be used aswell if (pendingStateNum > rollup.lastPendingState) { revert PendingStateDoesNotExist(); } // Check choosen pending state PendingState storage currentPendingState = rollup .pendingStateTransitions[pendingStateNum]; // Get oldStateRoot from pending batch oldStateRoot = currentPendingState.stateRoot; // Check initNumBatch matches the pending state if (initNumBatch != currentPendingState.lastVerifiedBatch) { revert InitNumBatchDoesNotMatchPendingState(); } } else { // Use consolidated state oldStateRoot = rollup.batchNumToStateRoot[initNumBatch]; if (oldStateRoot == bytes32(0)) { revert OldStateRootDoesNotExist(); } // Check initNumBatch is inside the range, sanity check if (initNumBatch > currentLastVerifiedBatch) { revert InitNumBatchAboveLastVerifiedBatch(); } } // Check final batch if (finalNewBatch <= currentLastVerifiedBatch) { revert FinalNumBatchBelowLastVerifiedBatch(); } // Get snark bytes bytes memory snarkHashBytes = _getInputSnarkBytes( rollup, initNumBatch, finalNewBatch, newLocalExitRoot, oldStateRoot, newStateRoot ); // Calulate the snark input uint256 inputSnark = uint256(sha256(snarkHashBytes)) % _RFIELD; // Verify proof if (!rollup.verifier.verifyProof(proof, [inputSnark])) { revert InvalidProof(); } // Pay POL rewards uint64 newVerifiedBatches = finalNewBatch - currentLastVerifiedBatch; pol.safeTransfer( beneficiary, calculateRewardPerBatch() * newVerifiedBatches ); // Update aggregation parameters totalVerifiedBatches += newVerifiedBatches; lastAggregationTimestamp = uint64(block.timestamp); // Callback to the rollup address rollup.rollupContract.onVerifyBatches( finalNewBatch, newStateRoot, msg.sender ); } /** * @notice Internal function to consolidate the state automatically once sequence or verify batches are called * It tries to consolidate the first and the middle pending state in the queue */ function _tryConsolidatePendingState(RollupData storage rollup) internal { // Check if there's any state to consolidate if (rollup.lastPendingState > rollup.lastPendingStateConsolidated) { // Check if it's possible to consolidate the next pending state uint64 nextPendingState = rollup.lastPendingStateConsolidated + 1; if (_isPendingStateConsolidable(rollup, nextPendingState)) { // Check middle pending state ( binary search of 1 step) uint64 middlePendingState = nextPendingState + (rollup.lastPendingState - nextPendingState) / 2; // Try to consolidate it, and if not, consolidate the nextPendingState if (_isPendingStateConsolidable(rollup, middlePendingState)) { _consolidatePendingState(rollup, middlePendingState); } else { _consolidatePendingState(rollup, nextPendingState); } } } } /** * @notice Allows to consolidate any pending state that has already exceed the pendingStateTimeout * Can be called by the trusted aggregator, which can consolidate any state without the timeout restrictions * @param rollupID Rollup identifier * @param pendingStateNum Pending state to consolidate */ function consolidatePendingState( uint32 rollupID, uint64 pendingStateNum ) external { RollupData storage rollup = rollupIDToRollupData[rollupID]; // Check if pending state can be consolidated // If trusted aggregator is the sender, do not check the timeout or the emergency state if (!hasRole(_TRUSTED_AGGREGATOR_ROLE, msg.sender)) { if (isEmergencyState) { revert OnlyNotEmergencyState(); } if (!_isPendingStateConsolidable(rollup, pendingStateNum)) { revert PendingStateNotConsolidable(); } } _consolidatePendingState(rollup, pendingStateNum); } /** * @notice Internal function to consolidate any pending state that has already exceed the pendingStateTimeout * @param rollup Rollup data storage pointer * @param pendingStateNum Pending state to consolidate */ function _consolidatePendingState( RollupData storage rollup, uint64 pendingStateNum ) internal { // Check if pendingStateNum is in correct range // - not consolidated (implicity checks that is not 0) // - exist ( has been added) if ( pendingStateNum <= rollup.lastPendingStateConsolidated || pendingStateNum > rollup.lastPendingState ) { revert PendingStateInvalid(); } PendingState storage currentPendingState = rollup .pendingStateTransitions[pendingStateNum]; // Update state uint64 newLastVerifiedBatch = currentPendingState.lastVerifiedBatch; rollup.lastVerifiedBatch = newLastVerifiedBatch; rollup.batchNumToStateRoot[newLastVerifiedBatch] = currentPendingState .stateRoot; rollup.lastLocalExitRoot = currentPendingState.exitRoot; // Update pending state rollup.lastPendingStateConsolidated = pendingStateNum; // Interact with globalExitRootManager globalExitRootManager.updateExitRoot(getRollupExitRoot()); emit ConsolidatePendingState( rollupAddressToID[address(rollup.rollupContract)], newLastVerifiedBatch, currentPendingState.stateRoot, currentPendingState.exitRoot, pendingStateNum ); } ///////////////////////////////// // Soundness protection functions ///////////////////////////////// /** * @notice Allows the trusted aggregator to override the pending state * if it's possible to prove a different state root given the same batches * @param rollupID Rollup identifier * @param initPendingStateNum Init pending state, 0 if consolidated state is used * @param finalPendingStateNum Final pending state, that will be used to compare with the newStateRoot * @param initNumBatch Batch which the aggregator starts the verification * @param finalNewBatch Last batch aggregator intends to verify * @param newLocalExitRoot New local exit root once the batch is processed * @param newStateRoot New State root once the batch is processed * @param proof Fflonk proof */ function overridePendingState( uint32 rollupID, uint64 initPendingStateNum, uint64 finalPendingStateNum, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes32[24] calldata proof ) external onlyRole(_TRUSTED_AGGREGATOR_ROLE) { RollupData storage rollup = rollupIDToRollupData[rollupID]; _proveDistinctPendingState( rollup, initPendingStateNum, finalPendingStateNum, initNumBatch, finalNewBatch, newLocalExitRoot, newStateRoot, proof ); // Consolidate state rollup.lastVerifiedBatch = finalNewBatch; rollup.batchNumToStateRoot[finalNewBatch] = newStateRoot; rollup.lastLocalExitRoot = newLocalExitRoot; // Clean pending state if any if (rollup.lastPendingState > 0) { rollup.lastPendingState = 0; rollup.lastPendingStateConsolidated = 0; } // Interact with globalExitRootManager globalExitRootManager.updateExitRoot(getRollupExitRoot()); // Update trusted aggregator timeout to max trustedAggregatorTimeout = _HALT_AGGREGATION_TIMEOUT; emit OverridePendingState( rollupID, finalNewBatch, newStateRoot, newLocalExitRoot, msg.sender ); } /** * @notice Allows activate the emergency state if its possible to prove a different state root given the same batches * @param rollupID Rollup identifier * @param initPendingStateNum Init pending state, 0 if consolidated state is used * @param finalPendingStateNum Final pending state, that will be used to compare with the newStateRoot * @param initNumBatch Batch which the aggregator starts the verification * @param finalNewBatch Last batch aggregator intends to verify * @param newLocalExitRoot New local exit root once the batch is processed * @param newStateRoot New State root once the batch is processed * @param proof Fflonk proof */ function proveNonDeterministicPendingState( uint32 rollupID, uint64 initPendingStateNum, uint64 finalPendingStateNum, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes32[24] calldata proof ) external ifNotEmergencyState { RollupData storage rollup = rollupIDToRollupData[rollupID]; _proveDistinctPendingState( rollup, initPendingStateNum, finalPendingStateNum, initNumBatch, finalNewBatch, newLocalExitRoot, newStateRoot, proof ); emit ProveNonDeterministicPendingState( rollup.pendingStateTransitions[finalPendingStateNum].stateRoot, newStateRoot ); // Activate emergency state _activateEmergencyState(); } /** * @notice Internal function that proves a different state root given the same batches to verify * @param rollup Rollup Data struct that will be checked * @param initPendingStateNum Init pending state, 0 if consolidated state is used * @param finalPendingStateNum Final pending state, that will be used to compare with the newStateRoot * @param initNumBatch Batch which the aggregator starts the verification * @param finalNewBatch Last batch aggregator intends to verify * @param newLocalExitRoot New local exit root once the batch is processed * @param newStateRoot New State root once the batch is processed * @param proof Fflonk proof */ function _proveDistinctPendingState( RollupData storage rollup, uint64 initPendingStateNum, uint64 finalPendingStateNum, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes32[24] calldata proof ) internal view virtual { bytes32 oldStateRoot; if (initNumBatch < rollup.lastVerifiedBatchBeforeUpgrade) { revert InitBatchMustMatchCurrentForkID(); } // Use pending state if specified, otherwise use consolidated state if (initPendingStateNum != 0) { // Check that pending state exist // Already consolidated pending states can be used aswell if (initPendingStateNum > rollup.lastPendingState) { revert PendingStateDoesNotExist(); } // Check choosen pending state PendingState storage initPendingState = rollup .pendingStateTransitions[initPendingStateNum]; // Get oldStateRoot from init pending state oldStateRoot = initPendingState.stateRoot; // Check initNumBatch matches the init pending state if (initNumBatch != initPendingState.lastVerifiedBatch) { revert InitNumBatchDoesNotMatchPendingState(); } } else { // Use consolidated state oldStateRoot = rollup.batchNumToStateRoot[initNumBatch]; if (oldStateRoot == bytes32(0)) { revert OldStateRootDoesNotExist(); } // Check initNumBatch is inside the range, sanity check if (initNumBatch > rollup.lastVerifiedBatch) { revert InitNumBatchAboveLastVerifiedBatch(); } } // Assert final pending state num is in correct range // - exist ( has been added) // - bigger than the initPendingstate // - not consolidated if ( finalPendingStateNum > rollup.lastPendingState || finalPendingStateNum <= initPendingStateNum || finalPendingStateNum <= rollup.lastPendingStateConsolidated ) { revert FinalPendingStateNumInvalid(); } // Check final num batch if ( finalNewBatch != rollup .pendingStateTransitions[finalPendingStateNum] .lastVerifiedBatch ) { revert FinalNumBatchDoesNotMatchPendingState(); } // Get snark bytes bytes memory snarkHashBytes = _getInputSnarkBytes( rollup, initNumBatch, finalNewBatch, newLocalExitRoot, oldStateRoot, newStateRoot ); // Calulate the snark input uint256 inputSnark = uint256(sha256(snarkHashBytes)) % _RFIELD; // Verify proof if (!rollup.verifier.verifyProof(proof, [inputSnark])) { revert InvalidProof(); } if ( rollup.pendingStateTransitions[finalPendingStateNum].stateRoot == newStateRoot ) { revert StoredRootMustBeDifferentThanNewRoot(); } } /** * @notice Function to update the batch fee based on the new verified batches * The batch fee will not be updated when the trusted aggregator verifies batches * @param newLastVerifiedBatch New last verified batch */ function _updateBatchFee( RollupData storage rollup, uint64 newLastVerifiedBatch ) internal { uint64 currentLastVerifiedBatch = _getLastVerifiedBatch(rollup); uint64 currentBatch = newLastVerifiedBatch; uint256 totalBatchesAboveTarget; uint256 newBatchesVerified = newLastVerifiedBatch - currentLastVerifiedBatch; uint256 targetTimestamp = block.timestamp - verifyBatchTimeTarget; while (currentBatch != currentLastVerifiedBatch) { // Load sequenced batchdata SequencedBatchData storage currentSequencedBatchData = rollup .sequencedBatches[currentBatch]; // Check if timestamp is below the verifyBatchTimeTarget if ( targetTimestamp < currentSequencedBatchData.sequencedTimestamp ) { // update currentBatch currentBatch = currentSequencedBatchData .previousLastBatchSequenced; } else { // The rest of batches will be above totalBatchesAboveTarget = currentBatch - currentLastVerifiedBatch; break; } } uint256 totalBatchesBelowTarget = newBatchesVerified - totalBatchesAboveTarget; // _MAX_BATCH_FEE --> (< 70 bits) // multiplierBatchFee --> (< 10 bits) // _MAX_BATCH_MULTIPLIER = 12 // multiplierBatchFee ** _MAX_BATCH_MULTIPLIER --> (< 128 bits) // batchFee * (multiplierBatchFee ** _MAX_BATCH_MULTIPLIER)--> // (< 70 bits) * (< 128 bits) = < 256 bits // Since all the following operations cannot overflow, we can optimize this operations with unchecked unchecked { if (totalBatchesBelowTarget < totalBatchesAboveTarget) { // There are more batches above target, fee is multiplied uint256 diffBatches = totalBatchesAboveTarget - totalBatchesBelowTarget; diffBatches = diffBatches > _MAX_BATCH_MULTIPLIER ? _MAX_BATCH_MULTIPLIER : diffBatches; // For every multiplierBatchFee multiplication we must shift 3 zeroes since we have 3 decimals _batchFee = (_batchFee * (uint256(multiplierBatchFee) ** diffBatches)) / (uint256(1000) ** diffBatches); } else { // There are more batches below target, fee is divided uint256 diffBatches = totalBatchesBelowTarget - totalBatchesAboveTarget; diffBatches = diffBatches > _MAX_BATCH_MULTIPLIER ? _MAX_BATCH_MULTIPLIER : diffBatches; // For every multiplierBatchFee multiplication we must shift 3 zeroes since we have 3 decimals uint256 accDivisor = (uint256(1 ether) * (uint256(multiplierBatchFee) ** diffBatches)) / (uint256(1000) ** diffBatches); // multiplyFactor = multiplierBatchFee ** diffBatches / 10 ** (diffBatches * 3) // accDivisor = 1E18 * multiplyFactor // 1E18 * batchFee / accDivisor = batchFee / multiplyFactor // < 60 bits * < 70 bits / ~60 bits --> overflow not possible _batchFee = (uint256(1 ether) * _batchFee) / accDivisor; } } // Batch fee must remain inside a range if (_batchFee > _MAX_BATCH_FEE) { _batchFee = _MAX_BATCH_FEE; } else if (_batchFee < _MIN_BATCH_FEE) { _batchFee = _MIN_BATCH_FEE; } } //////////////////////// // Emergency state functions //////////////////////// /** * @notice Function to activate emergency state, which also enables the emergency mode on both PolygonRollupManager and PolygonZkEVMBridge contracts * If not called by the owner must not have been aggregated in a _HALT_AGGREGATION_TIMEOUT period and an emergency state was not happened in the same period */ function activateEmergencyState() external { if (!hasRole(_EMERGENCY_COUNCIL_ROLE, msg.sender)) { if ( lastAggregationTimestamp == 0 || lastAggregationTimestamp + _HALT_AGGREGATION_TIMEOUT > block.timestamp || lastDeactivatedEmergencyStateTimestamp + _HALT_AGGREGATION_TIMEOUT > block.timestamp ) { revert HaltTimeoutNotExpired(); } } _activateEmergencyState(); } /** * @notice Function to deactivate emergency state on both PolygonRollupManager and PolygonZkEVMBridge contracts */ function deactivateEmergencyState() external onlyRole(_STOP_EMERGENCY_ROLE) { // Set last deactivated emergency state lastDeactivatedEmergencyStateTimestamp = uint64(block.timestamp); // Deactivate emergency state on PolygonZkEVMBridge bridgeAddress.deactivateEmergencyState(); // Deactivate emergency state on this contract super._deactivateEmergencyState(); } /** * @notice Internal function to activate emergency state on both PolygonRollupManager and PolygonZkEVMBridge contracts */ function _activateEmergencyState() internal override { // Activate emergency state on PolygonZkEVM Bridge bridgeAddress.activateEmergencyState(); // Activate emergency state on this contract super._activateEmergencyState(); } ////////////////// // Setter functions ////////////////// /** * @notice Set a new pending state timeout * The timeout can only be lowered, except if emergency state is active * @param newTrustedAggregatorTimeout Trusted aggregator timeout */ function setTrustedAggregatorTimeout( uint64 newTrustedAggregatorTimeout ) external onlyRole(_TWEAK_PARAMETERS_ROLE) { if (!isEmergencyState) { if (newTrustedAggregatorTimeout >= trustedAggregatorTimeout) { revert NewTrustedAggregatorTimeoutMustBeLower(); } } trustedAggregatorTimeout = newTrustedAggregatorTimeout; emit SetTrustedAggregatorTimeout(newTrustedAggregatorTimeout); } /** * @notice Set a new trusted aggregator timeout * The timeout can only be lowered, except if emergency state is active * @param newPendingStateTimeout Trusted aggregator timeout */ function setPendingStateTimeout( uint64 newPendingStateTimeout ) external onlyRole(_TWEAK_PARAMETERS_ROLE) { if (!isEmergencyState) { if (newPendingStateTimeout >= pendingStateTimeout) { revert NewPendingStateTimeoutMustBeLower(); } } pendingStateTimeout = newPendingStateTimeout; emit SetPendingStateTimeout(newPendingStateTimeout); } /** * @notice Set a new multiplier batch fee * @param newMultiplierBatchFee multiplier batch fee */ function setMultiplierBatchFee( uint16 newMultiplierBatchFee ) external onlyRole(_TWEAK_PARAMETERS_ROLE) { if (newMultiplierBatchFee < 1000 || newMultiplierBatchFee > 1023) { revert InvalidRangeMultiplierBatchFee(); } multiplierBatchFee = newMultiplierBatchFee; emit SetMultiplierBatchFee(newMultiplierBatchFee); } /** * @notice Set a new verify batch time target * This value will only be relevant once the aggregation is decentralized, so * the trustedAggregatorTimeout should be zero or very close to zero * @param newVerifyBatchTimeTarget Verify batch time target */ function setVerifyBatchTimeTarget( uint64 newVerifyBatchTimeTarget ) external onlyRole(_TWEAK_PARAMETERS_ROLE) { if (newVerifyBatchTimeTarget > 1 days) { revert InvalidRangeBatchTimeTarget(); } verifyBatchTimeTarget = newVerifyBatchTimeTarget; emit SetVerifyBatchTimeTarget(newVerifyBatchTimeTarget); } /** * @notice Set the current batch fee * @param newBatchFee new batch fee */ function setBatchFee(uint256 newBatchFee) external onlyRole(_SET_FEE_ROLE) { // check fees min and max if (newBatchFee > _MAX_BATCH_FEE || newBatchFee < _MIN_BATCH_FEE) { revert BatchFeeOutOfRange(); } _batchFee = newBatchFee; emit SetBatchFee(newBatchFee); } //////////////////////// // view/pure functions /////////////////////// /** * @notice Get the current rollup exit root * Compute using all the local exit roots of all rollups the rollup exit root * Since it's expected to have no more than 10 rollups in this first version, even if this approach * has a gas consumption that scales linearly with the rollups added, it's ok * In a future versions this computation will be done inside the circuit */ function getRollupExitRoot() public view returns (bytes32) { uint256 currentNodes = rollupCount; // If there are no nodes return 0 if (currentNodes == 0) { return bytes32(0); } // This array will contain the nodes of the current iteration bytes32[] memory tmpTree = new bytes32[](currentNodes); // In the first iteration the nodes will be the leafs which are the local exit roots of each network for (uint256 i = 0; i < currentNodes; i++) { // The first rollup ID starts on 1 tmpTree[i] = rollupIDToRollupData[uint32(i + 1)].lastLocalExitRoot; } // This variable will keep track of the zero hashes bytes32 currentZeroHashHeight = 0; // This variable will keep track of the reamining levels to compute uint256 remainingLevels = _EXIT_TREE_DEPTH; // Calculate the root of the sub-tree that contains all the localExitRoots while (currentNodes != 1) { uint256 nextIterationNodes = currentNodes / 2 + (currentNodes % 2); bytes32[] memory nextTmpTree = new bytes32[](nextIterationNodes); for (uint256 i = 0; i < nextIterationNodes; i++) { // if we are on the last iteration of the current level and the nodes are odd if (i == nextIterationNodes - 1 && (currentNodes % 2) == 1) { nextTmpTree[i] = keccak256( abi.encodePacked(tmpTree[i * 2], currentZeroHashHeight) ); } else { nextTmpTree[i] = keccak256( abi.encodePacked(tmpTree[i * 2], tmpTree[(i * 2) + 1]) ); } } // Update tree variables tmpTree = nextTmpTree; currentNodes = nextIterationNodes; currentZeroHashHeight = keccak256( abi.encodePacked(currentZeroHashHeight, currentZeroHashHeight) ); remainingLevels--; } bytes32 currentRoot = tmpTree[0]; // Calculate remaining levels, since it's a sequencial merkle tree, the rest of the tree are zeroes for (uint256 i = 0; i < remainingLevels; i++) { currentRoot = keccak256( abi.encodePacked(currentRoot, currentZeroHashHeight) ); currentZeroHashHeight = keccak256( abi.encodePacked(currentZeroHashHeight, currentZeroHashHeight) ); } return currentRoot; } /** * @notice Get the last verified batch */ function getLastVerifiedBatch( uint32 rollupID ) public view returns (uint64) { return _getLastVerifiedBatch(rollupIDToRollupData[rollupID]); } /** * @notice Get the last verified batch */ function _getLastVerifiedBatch( RollupData storage rollup ) internal view returns (uint64) { if (rollup.lastPendingState > 0) { return rollup .pendingStateTransitions[rollup.lastPendingState] .lastVerifiedBatch; } else { return rollup.lastVerifiedBatch; } } /** * @notice Returns a boolean that indicates if the pendingStateNum is or not consolidable * @param rollupID Rollup id * @param pendingStateNum Pending state number to check * Note that his function does not check if the pending state currently exists, or if it's consolidated already */ function isPendingStateConsolidable( uint32 rollupID, uint64 pendingStateNum ) public view returns (bool) { return _isPendingStateConsolidable( rollupIDToRollupData[rollupID], pendingStateNum ); } /** * @notice Returns a boolean that indicates if the pendingStateNum is or not consolidable * @param rollup Rollup data storage pointer * @param pendingStateNum Pending state number to check * Note that his function does not check if the pending state currently exists, or if it's consolidated already */ function _isPendingStateConsolidable( RollupData storage rollup, uint64 pendingStateNum ) internal view returns (bool) { return (rollup.pendingStateTransitions[pendingStateNum].timestamp + pendingStateTimeout <= block.timestamp); } /** * @notice Function to calculate the reward to verify a single batch */ function calculateRewardPerBatch() public view returns (uint256) { uint256 currentBalance = pol.balanceOf(address(this)); // Total Batches to be verified = total Sequenced Batches - total verified Batches uint256 totalBatchesToVerify = totalSequencedBatches - totalVerifiedBatches; if (totalBatchesToVerify == 0) return 0; return currentBalance / totalBatchesToVerify; } /** * @notice Get batch fee * This function is used instad of the automatic public view one, * because in a future might change the behaviour and we will be able to mantain the interface */ function getBatchFee() public view returns (uint256) { return _batchFee; } /** * @notice Get forced batch fee */ function getForcedBatchFee() public view returns (uint256) { return _batchFee * 100; } /** * @notice Function to calculate the input snark bytes * @param rollupID Rollup id used to calculate the input snark bytes * @param initNumBatch Batch which the aggregator starts the verification * @param finalNewBatch Last batch aggregator intends to verify * @param newLocalExitRoot New local exit root once the batch is processed * @param oldStateRoot State root before batch is processed * @param newStateRoot New State root once the batch is processed */ function getInputSnarkBytes( uint32 rollupID, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 oldStateRoot, bytes32 newStateRoot ) public view returns (bytes memory) { return _getInputSnarkBytes( rollupIDToRollupData[rollupID], initNumBatch, finalNewBatch, newLocalExitRoot, oldStateRoot, newStateRoot ); } /** * @notice Function to calculate the input snark bytes * @param rollup Rollup data storage pointer * @param initNumBatch Batch which the aggregator starts the verification * @param finalNewBatch Last batch aggregator intends to verify * @param newLocalExitRoot New local exit root once the batch is processed * @param oldStateRoot State root before batch is processed * @param newStateRoot New State root once the batch is processed */ function _getInputSnarkBytes( RollupData storage rollup, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 oldStateRoot, bytes32 newStateRoot ) internal view returns (bytes memory) { // Sanity check bytes32 oldAccInputHash = rollup .sequencedBatches[initNumBatch] .accInputHash; bytes32 newAccInputHash = rollup .sequencedBatches[finalNewBatch] .accInputHash; // Sanity check if (initNumBatch != 0 && oldAccInputHash == bytes32(0)) { revert OldAccInputHashDoesNotExist(); } if (newAccInputHash == bytes32(0)) { revert NewAccInputHashDoesNotExist(); } // Check that new state root is inside goldilocks field if (!_checkStateRootInsidePrime(uint256(newStateRoot))) { revert NewStateRootNotInsidePrime(); } return abi.encodePacked( msg.sender, oldStateRoot, oldAccInputHash, initNumBatch, rollup.chainID, rollup.forkID, newStateRoot, newAccInputHash, newLocalExitRoot, finalNewBatch ); } /** * @notice Function to check if the state root is inside of the prime field * @param newStateRoot New State root once the batch is processed */ function _checkStateRootInsidePrime( uint256 newStateRoot ) internal pure returns (bool) { if ( ((newStateRoot & _MAX_UINT_64) < _GOLDILOCKS_PRIME_FIELD) && (((newStateRoot >> 64) & _MAX_UINT_64) < _GOLDILOCKS_PRIME_FIELD) && (((newStateRoot >> 128) & _MAX_UINT_64) < _GOLDILOCKS_PRIME_FIELD) && ((newStateRoot >> 192) < _GOLDILOCKS_PRIME_FIELD) ) { return true; } else { return false; } } /** * @notice Get rollup state root given a batch number * @param rollupID Rollup identifier * @param batchNum Batch number */ function getRollupBatchNumToStateRoot( uint32 rollupID, uint64 batchNum ) public view returns (bytes32) { return rollupIDToRollupData[rollupID].batchNumToStateRoot[batchNum]; } /** * @notice Get rollup sequence batches struct given a batch number * @param rollupID Rollup identifier * @param batchNum Batch number */ function getRollupSequencedBatches( uint32 rollupID, uint64 batchNum ) public view returns (SequencedBatchData memory) { return rollupIDToRollupData[rollupID].sequencedBatches[batchNum]; } /** * @notice Get rollup sequence pending state struct given a batch number * @param rollupID Rollup identifier * @param batchNum Batch number */ function getRollupPendingStateTransitions( uint32 rollupID, uint64 batchNum ) public view returns (PendingState memory) { return rollupIDToRollupData[rollupID].pendingStateTransitions[batchNum]; } }
{ "optimizer": { "enabled": true, "runs": 999999 }, "evmVersion": "shanghai", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IPolygonZkEVMGlobalExitRootV2","name":"_globalExitRootManager","type":"address"},{"internalType":"contract IERC20Upgradeable","name":"_pol","type":"address"},{"internalType":"contract IPolygonZkEVMBridgeV2","name":"_bridgeAddress","type":"address"},{"internalType":"contract PolygonRollupManager","name":"_rollupManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BatchAlreadyVerified","type":"error"},{"inputs":[],"name":"BatchNotSequencedOrNotSequenceEnd","type":"error"},{"inputs":[],"name":"ExceedMaxVerifyBatches","type":"error"},{"inputs":[],"name":"FinalNumBatchBelowLastVerifiedBatch","type":"error"},{"inputs":[],"name":"FinalNumBatchDoesNotMatchPendingState","type":"error"},{"inputs":[],"name":"FinalPendingStateNumInvalid","type":"error"},{"inputs":[],"name":"ForceBatchNotAllowed","type":"error"},{"inputs":[],"name":"ForceBatchTimeoutNotExpired","type":"error"},{"inputs":[],"name":"ForceBatchesAlreadyActive","type":"error"},{"inputs":[],"name":"ForceBatchesDecentralized","type":"error"},{"inputs":[],"name":"ForceBatchesNotAllowedOnEmergencyState","type":"error"},{"inputs":[],"name":"ForceBatchesOverflow","type":"error"},{"inputs":[],"name":"ForcedDataDoesNotMatch","type":"error"},{"inputs":[],"name":"GasTokenNetworkMustBeZeroOnEther","type":"error"},{"inputs":[],"name":"GlobalExitRootNotExist","type":"error"},{"inputs":[],"name":"HaltTimeoutNotExpired","type":"error"},{"inputs":[],"name":"HaltTimeoutNotExpiredAfterEmergencyState","type":"error"},{"inputs":[],"name":"HugeTokenMetadataNotSupported","type":"error"},{"inputs":[],"name":"InitNumBatchAboveLastVerifiedBatch","type":"error"},{"inputs":[],"name":"InitNumBatchDoesNotMatchPendingState","type":"error"},{"inputs":[],"name":"InitSequencedBatchDoesNotMatch","type":"error"},{"inputs":[],"name":"InvalidInitializeTransaction","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"InvalidRangeBatchTimeTarget","type":"error"},{"inputs":[],"name":"InvalidRangeForceBatchTimeout","type":"error"},{"inputs":[],"name":"InvalidRangeMultiplierBatchFee","type":"error"},{"inputs":[],"name":"MaxTimestampSequenceInvalid","type":"error"},{"inputs":[],"name":"NewAccInputHashDoesNotExist","type":"error"},{"inputs":[],"name":"NewPendingStateTimeoutMustBeLower","type":"error"},{"inputs":[],"name":"NewStateRootNotInsidePrime","type":"error"},{"inputs":[],"name":"NewTrustedAggregatorTimeoutMustBeLower","type":"error"},{"inputs":[],"name":"NotEnoughMaticAmount","type":"error"},{"inputs":[],"name":"NotEnoughPOLAmount","type":"error"},{"inputs":[],"name":"OldAccInputHashDoesNotExist","type":"error"},{"inputs":[],"name":"OldStateRootDoesNotExist","type":"error"},{"inputs":[],"name":"OnlyAdmin","type":"error"},{"inputs":[],"name":"OnlyPendingAdmin","type":"error"},{"inputs":[],"name":"OnlyRollupManager","type":"error"},{"inputs":[],"name":"OnlyTrustedAggregator","type":"error"},{"inputs":[],"name":"OnlyTrustedSequencer","type":"error"},{"inputs":[],"name":"PendingStateDoesNotExist","type":"error"},{"inputs":[],"name":"PendingStateInvalid","type":"error"},{"inputs":[],"name":"PendingStateNotConsolidable","type":"error"},{"inputs":[],"name":"PendingStateTimeoutExceedHaltAggregationTimeout","type":"error"},{"inputs":[],"name":"SequenceWithDataAvailabilityNotAllowed","type":"error"},{"inputs":[],"name":"SequenceZeroBatches","type":"error"},{"inputs":[],"name":"SequencedTimestampBelowForcedTimestamp","type":"error"},{"inputs":[],"name":"SequencedTimestampInvalid","type":"error"},{"inputs":[],"name":"StoredRootMustBeDifferentThanNewRoot","type":"error"},{"inputs":[],"name":"SwitchToSameValue","type":"error"},{"inputs":[],"name":"TransactionsLengthAboveMax","type":"error"},{"inputs":[],"name":"TrustedAggregatorTimeoutExceedHaltAggregationTimeout","type":"error"},{"inputs":[],"name":"TrustedAggregatorTimeoutNotExpired","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AcceptAdminRole","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"forceBatchNum","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"lastGlobalExitRoot","type":"bytes32"},{"indexed":false,"internalType":"address","name":"sequencer","type":"address"},{"indexed":false,"internalType":"bytes","name":"transactions","type":"bytes"}],"name":"ForceBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"transactions","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"lastGlobalExitRoot","type":"bytes32"},{"indexed":false,"internalType":"address","name":"sequencer","type":"address"}],"name":"InitialSequenceBatches","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"numBatch","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"l1InfoRoot","type":"bytes32"}],"name":"SequenceBatches","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"numBatch","type":"uint64"}],"name":"SequenceForceBatches","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newDataAvailabilityProtocol","type":"address"}],"name":"SetDataAvailabilityProtocol","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newForceBatchAddress","type":"address"}],"name":"SetForceBatchAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"newforceBatchTimeout","type":"uint64"}],"name":"SetForceBatchTimeout","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newTrustedSequencer","type":"address"}],"name":"SetTrustedSequencer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"newTrustedSequencerURL","type":"string"}],"name":"SetTrustedSequencerURL","type":"event"},{"anonymous":false,"inputs":[],"name":"SwitchSequenceWithDataAvailability","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"TransferAdminRole","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"numBatch","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"indexed":true,"internalType":"address","name":"aggregator","type":"address"}],"name":"VerifyBatches","type":"event"},{"inputs":[],"name":"GLOBAL_EXIT_ROOT_MANAGER_L2","outputs":[{"internalType":"contract IBasePolygonZkEVMGlobalExitRoot","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIALIZE_TX_BRIDGE_LIST_LEN_LEN","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIALIZE_TX_BRIDGE_PARAMS","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS_EMPTY_METADATA","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIALIZE_TX_CONSTANT_BYTES","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIALIZE_TX_CONSTANT_BYTES_EMPTY_METADATA","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIALIZE_TX_DATA_LEN_EMPTY_METADATA","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIALIZE_TX_EFFECTIVE_PERCENTAGE","outputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SIGNATURE_INITIALIZE_TX_R","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SIGNATURE_INITIALIZE_TX_S","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SIGNATURE_INITIALIZE_TX_V","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIMESTAMP_RANGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptAdminRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bridgeAddress","outputs":[{"internalType":"contract IPolygonZkEVMBridgeV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"calculatePolPerForceBatch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dataAvailabilityProtocol","outputs":[{"internalType":"contract IDataAvailabilityProtocol","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"transactions","type":"bytes"},{"internalType":"uint256","name":"polAmount","type":"uint256"}],"name":"forceBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"forceBatchAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forceBatchTimeout","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"forcedBatches","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasTokenNetwork","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"networkID","type":"uint32"},{"internalType":"address","name":"_gasTokenAddress","type":"address"},{"internalType":"uint32","name":"_gasTokenNetwork","type":"uint32"},{"internalType":"bytes","name":"_gasTokenMetadata","type":"bytes"}],"name":"generateInitializeTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalExitRootManager","outputs":[{"internalType":"contract IPolygonZkEVMGlobalExitRootV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"sequencer","type":"address"},{"internalType":"uint32","name":"networkID","type":"uint32"},{"internalType":"address","name":"_gasTokenAddress","type":"address"},{"internalType":"string","name":"sequencerURL","type":"string"},{"internalType":"string","name":"_networkName","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initializeMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isSequenceWithDataAvailabilityAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastAccInputHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastForceBatch","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastForceBatchSequenced","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"networkName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"lastVerifiedBatch","type":"uint64"},{"internalType":"bytes32","name":"newStateRoot","type":"bytes32"},{"internalType":"address","name":"aggregator","type":"address"}],"name":"onVerifyBatches","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pol","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rollupManager","outputs":[{"internalType":"contract PolygonRollupManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"transactions","type":"bytes"},{"internalType":"bytes32","name":"forcedGlobalExitRoot","type":"bytes32"},{"internalType":"uint64","name":"forcedTimestamp","type":"uint64"},{"internalType":"bytes32","name":"forcedBlockHashL1","type":"bytes32"}],"internalType":"struct PolygonRollupBaseEtrogNoGap.BatchData[]","name":"batches","type":"tuple[]"},{"internalType":"uint64","name":"maxSequenceTimestamp","type":"uint64"},{"internalType":"uint64","name":"initSequencedBatch","type":"uint64"},{"internalType":"address","name":"l2Coinbase","type":"address"}],"name":"sequenceBatches","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"transactionsHash","type":"bytes32"},{"internalType":"bytes32","name":"forcedGlobalExitRoot","type":"bytes32"},{"internalType":"uint64","name":"forcedTimestamp","type":"uint64"},{"internalType":"bytes32","name":"forcedBlockHashL1","type":"bytes32"}],"internalType":"struct PolygonValidiumStorageMigration.ValidiumBatchData[]","name":"batches","type":"tuple[]"},{"internalType":"uint64","name":"maxSequenceTimestamp","type":"uint64"},{"internalType":"uint64","name":"initSequencedBatch","type":"uint64"},{"internalType":"address","name":"l2Coinbase","type":"address"},{"internalType":"bytes","name":"dataAvailabilityMessage","type":"bytes"}],"name":"sequenceBatchesValidium","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"transactions","type":"bytes"},{"internalType":"bytes32","name":"forcedGlobalExitRoot","type":"bytes32"},{"internalType":"uint64","name":"forcedTimestamp","type":"uint64"},{"internalType":"bytes32","name":"forcedBlockHashL1","type":"bytes32"}],"internalType":"struct PolygonRollupBaseEtrogNoGap.BatchData[]","name":"batches","type":"tuple[]"}],"name":"sequenceForceBatches","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IDataAvailabilityProtocol","name":"newDataAvailabilityProtocol","type":"address"}],"name":"setDataAvailabilityProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newForceBatchAddress","type":"address"}],"name":"setForceBatchAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"newforceBatchTimeout","type":"uint64"}],"name":"setForceBatchTimeout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newTrustedSequencer","type":"address"}],"name":"setTrustedSequencer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newTrustedSequencerURL","type":"string"}],"name":"setTrustedSequencerURL","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"newIsSequenceWithDataAvailabilityAllowed","type":"bool"}],"name":"switchSequenceWithDataAvailability","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"transferAdminRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedSequencer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedSequencerURL","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
61010060405234801562000011575f80fd5b50604051620053123803806200531283398101604081905262000034916200006f565b6001600160a01b0393841660a052918316608052821660c0521660e052620000d4565b6001600160a01b03811681146200006c575f80fd5b50565b5f805f806080858703121562000083575f80fd5b8451620000908162000057565b6020860151909450620000a38162000057565b6040860151909350620000b68162000057565b6060860151909250620000c98162000057565b939692955090935050565b60805160a05160c05160e05161512a620001e85f395f818161054e01528181610a2d01528181610d6901528181610ed501528181611116015281816116e301528181611c47015281816120960152818161218b01528181612d6d01528181612dec01528181612e0e01528181612faa015281816131b50152818161327a01528181613d0c01528181613d8501528181613da70152613e4f01525f81816106ef01528181611305015281816113da015281816123550152818161245d015281816128a8015261385a01525f81816107ab0152818161155a01528181612924015281816133c201526138d601525f8181610803015281816108e0015281816120df01528181612eba0152613397015261512a5ff3fe608060405234801561000f575f80fd5b5060043610610319575f3560e01c806371257022116101a8578063c7fffd4b116100f3578063def57e541161009e578063e7a7ed0211610079578063e7a7ed0214610845578063eaeb077b14610859578063f35dda471461086c578063f851a44014610874575f80fd5b8063def57e54146107eb578063e46761c4146107fe578063e57a0b4c14610825575f80fd5b8063d02103ca116100ce578063d02103ca146107a6578063d7bc90ff146107cd578063db5b0ed7146107d8575f80fd5b8063c7fffd4b1461076b578063c89e42df14610773578063cfa8ed4714610786575f80fd5b80639f26f84011610153578063ada8f9191161012e578063ada8f91914610724578063b0afe15414610737578063c754c7ed14610743575f80fd5b80639f26f840146106d7578063a3c573eb146106ea578063a652f26c14610711575f80fd5b80638c3d7301116101835780638c3d7301146106a157806391cafe32146106a95780639e001877146106bc575f80fd5b8063712570221461063f5780637a5460c5146106525780637cd76b8b1461068e575f80fd5b806340b5de6c1161026857806352bdeb6d116102135780636b8616ce116101ee5780636b8616ce146106045780636e05d2cd146106235780636ff512cc1461062c575f80fd5b806352bdeb6d146105b8578063542028d5146105f4578063676870d2146105fc575f80fd5b806349b7b8021161024357806349b7b802146105495780634c21fef3146105705780634e487706146105a5575f80fd5b806340b5de6c146104b057806342308fab146105085780634560526714610510575f80fd5b806326782247116102c857806332c2d153116102a357806332c2d153146104405780633c351e10146104535780633cbc795b14610473575f80fd5b806326782247146103c85780632acdc2b61461040d5780632c111c0614610420575f80fd5b8063107bf28c116102f8578063107bf28c1461039c57806311e892d4146103a45780631c8b9370146103be575f80fd5b8062d0295d1461031d578063035089631461033857806305835f3714610353575b5f80fd5b610325610899565b6040519081526020015b60405180910390f35b610340602081565b60405161ffff909116815260200161032f565b61038f6040518060400160405280600881526020017f80808401c9c3809400000000000000000000000000000000000000000000000081525081565b60405161032f9190614259565b61038f61099f565b6103ac60f981565b60405160ff909116815260200161032f565b6103c6610a2b565b005b6001546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161032f565b6103c661041b366004614282565b610c49565b6008546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b6103c661044e3660046142ee565b610d67565b6009546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b60095461049b9074010000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff909116815260200161032f565b6104d77fff0000000000000000000000000000000000000000000000000000000000000081565b6040517fff00000000000000000000000000000000000000000000000000000000000000909116815260200161032f565b610325602481565b6007546105309068010000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff909116815260200161032f565b6103e87f000000000000000000000000000000000000000000000000000000000000000081565b603c546105959074010000000000000000000000000000000000000000900460ff1681565b604051901515815260200161032f565b6103c66105b336600461432d565b610e36565b61038f6040518060400160405280600281526020017f80b800000000000000000000000000000000000000000000000000000000000081525081565b61038f61103e565b610340601f81565b61032561061236600461432d565b60066020525f908152604090205481565b61032560055481565b6103c661063a366004614348565b61104b565b6103c661064d3660046144b0565b611114565b61038f6040518060400160405280600281526020017f80b900000000000000000000000000000000000000000000000000000000000081525081565b6103c661069c366004614348565b611918565b6103c66119e1565b6103c66106b7366004614348565b611ab3565b6103e873a40d5f56745a118d0906a34e69aec8c0db1cb8fa81565b6103c66106e536600461459f565b611bcb565b6103e87f000000000000000000000000000000000000000000000000000000000000000081565b61038f61071f3660046145de565b612257565b6103c6610732366004614348565b612635565b6103256405ca1ab1e081565b60075461053090700100000000000000000000000000000000900467ffffffffffffffff1681565b6103ac60e481565b6103c661078136600461464f565b6126fe565b6002546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b6103e87f000000000000000000000000000000000000000000000000000000000000000081565b610325635ca1ab1e81565b6103c66107e63660046146bf565b612790565b6103c66107f9366004614788565b6130d6565b6103e87f000000000000000000000000000000000000000000000000000000000000000081565b603c546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b6007546105309067ffffffffffffffff1681565b6103c6610867366004614800565b61313e565b6103ac601b81565b5f546103e89062010000900473ffffffffffffffffffffffffffffffffffffffff1681565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f90819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610925573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109499190614848565b6007549091505f906109739067ffffffffffffffff6801000000000000000082048116911661488c565b67ffffffffffffffff169050805f0361098e575f9250505090565b61099881836148b4565b9250505090565b600480546109ac906148ec565b80601f01602080910402602001604051908101604052809291908181526020018280546109d8906148ec565b8015610a235780601f106109fa57610100808354040283529160200191610a23565b820191905f5260205f20905b815481529060010190602001808311610a0657829003601f168201915b505050505081565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163314610a9a576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54600290610100900460ff16158015610aba57505f5460ff8083169116105b610b4b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b5f8054600a8054603c80547401000000000000000000000000000000000000000080840460ff90811615159091027fffffffffffffffffffffff00000000000000000000000000000000000000000092831673ffffffffffffffffffffffffffffffffffffffff861617179092559091169091557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909116908316908117610100177fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a150565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314610c9f576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603c5474010000000000000000000000000000000000000000900460ff16151581151503610cf9576040517f5f0e7abe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603c80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000831515021790556040517ff32a0473f809a720a4f8af1e50d353f1caf7452030626fdaac4273f5e6587f41905f90a150565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163314610dd6576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff168367ffffffffffffffff167f9c72852172521097ba7e1482e6b44b351323df0155f97f4ea18fcec28e1f596684604051610e2991815260200190565b60405180910390a3505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314610e8c576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62093a8067ffffffffffffffff82161115610ed3576040517ff5e37f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f3c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f60919061493d565b610fc15760075467ffffffffffffffff700100000000000000000000000000000000909104811690821610610fc1576040517ff5e37f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000067ffffffffffffffff8416908102919091179091556040519081527fa7eb6cb8a613eb4e8bddc1ac3d61ec6cf10898760f0b187bcca794c6ca6fa40b90602001610c3e565b600380546109ac906148ec565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff1633146110a1576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527ff54144f9611984021529f814a1cb6a41e22c58351510a0d9f7e822618abb9cc090602001610c3e565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163314611183576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54610100900460ff16158080156111a157505f54600160ff909116105b806111ba5750303b1580156111ba57505f5460ff166001145b611246576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b42565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156112a2575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b606073ffffffffffffffffffffffffffffffffffffffff8516156114ff576040517fc00f14ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063c00f14ab906024015f60405180830381865afa158015611349573d5f803e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261138e9190810190614958565b6040517f318aee3d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87811660048301529192505f9182917f00000000000000000000000000000000000000000000000000000000000000009091169063318aee3d906024016040805180830381865afa158015611420573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061144491906149ca565b915091508163ffffffff165f146114bb576009805463ffffffff841674010000000000000000000000000000000000000000027fffffffffffffffff00000000000000000000000000000000000000000000000090911673ffffffffffffffffffffffffffffffffffffffff8416171790556114fc565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff89161790555b50505b6009545f9061154690889073ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900463ffffffff1685612257565b90505f818051906020012090505f4290505f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115c1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115e59190614848565b90505f808483858f6115f8600143614a02565b60408051602081019790975286019490945260608086019390935260c09190911b7fffffffffffffffff000000000000000000000000000000000000000000000000166080850152901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016608883015240609c82015260bc01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815290829052805160209091012060058190557f9a908e73000000000000000000000000000000000000000000000000000000008252600160048301526024820181905291507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af115801561173e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117629190614a1b565b508c5f60026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508b60025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555088600390816117f29190614a7b565b5060046117ff8982614a7b565b508c60085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062069780600760106101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507f060116213bcbf54ca19fd649dc84b59ab2bbd200ab199770e4d923e222a28e7f85838e60405161189f93929190614b93565b60405180910390a1505050505050801561190f575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff16331461196e576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603c80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fd331bd4c4cd1afecb94a225184bded161ff3213624ba4fb58c4f30c5a861144a90602001610c3e565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a32576040517fd1ec4b2300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001545f80547fffffffffffffffffffff0000000000000000000000000000000000000000ffff1673ffffffffffffffffffffffffffffffffffffffff9092166201000081029290921790556040519081527f056dc487bbf0795d0bbb1b4f0af523a855503cff740bfb4d5475f7a90c091e8e9060200160405180910390a1565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314611b09576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60085473ffffffffffffffffffffffffffffffffffffffff16611b58576040517fc89374d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f5fbd7dd171301c4a1611a84aac4ba86d119478560557755f7927595b082634fb90602001610c3e565b60085473ffffffffffffffffffffffffffffffffffffffff168015801590611c09575073ffffffffffffffffffffffffffffffffffffffff81163314155b15611c40576040517f24eff8c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4262093a807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166330c27dde6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cd29190614a1b565b611cdc9190614bd1565b67ffffffffffffffff161115611d1e576040517f3d49ed4c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f819003611d59576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e8811115611d95576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60075467ffffffffffffffff80821691611dbd91849168010000000000000000900416614bf2565b1115611df5576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007546005546801000000000000000090910467ffffffffffffffff16905f5b83811015612090575f878783818110611e3057611e30614c05565b9050602002810190611e429190614c32565b611e4b90614c6e565b905083611e5781614cd9565b825180516020918201208185015160408087015160608801519151959a509295505f94611ec3948794929101938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff89165f90815260069093529120549091508114611f4b576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff86165f90815260066020526040812055611f6f600188614a02565b8403611fde5742600760109054906101000a900467ffffffffffffffff168460400151611f9c9190614bd1565b67ffffffffffffffff161115611fde576040517fc44a082100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208381015160408086015160608088015183519586018b90529285018790528481019390935260c01b7fffffffffffffffff0000000000000000000000000000000000000000000000001660808401523390911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166088830152609c82015260bc01604051602081830303815290604052805190602001209450505050808061208890614cff565b915050611e15565b506121067f0000000000000000000000000000000000000000000000000000000000000000846120be610899565b6120c89190614d36565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169190613605565b60058190556007805467ffffffffffffffff841668010000000000000000027fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff9091161790556040517f9a908e730000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690639a908e73906121d7908790869060040167ffffffffffffffff929092168252602082015260400190565b6020604051808303815f875af11580156121f3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122179190614a1b565b60405190915067ffffffffffffffff8216907f648a61dd2438f072f5a1960939abd30f37aea80d2e94c9792ad142d3e0a490a4905f90a250505050505050565b60605f85858573a40d5f56745a118d0906a34e69aec8c0db1cb8fa5f8760405160240161228996959493929190614d4d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167ff811bff70000000000000000000000000000000000000000000000000000000017905283519091506060905f036123d95760f9601f835161231d9190614daf565b6040518060400160405280600881526020017f80808401c9c380940000000000000000000000000000000000000000000000008152507f00000000000000000000000000000000000000000000000000000000000000006040518060400160405280600281526020017f80b800000000000000000000000000000000000000000000000000000000000081525060e4876040516020016123c39796959493929190614dca565b60405160208183030381529060405290506124dd565b815161ffff1015612416576040517f248b8f8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815160f9612425602083614daf565b6040518060400160405280600881526020017f80808401c9c380940000000000000000000000000000000000000000000000008152507f00000000000000000000000000000000000000000000000000000000000000006040518060400160405280600281526020017f80b900000000000000000000000000000000000000000000000000000000000081525085886040516020016124ca9796959493929190614eac565b6040516020818303038152906040529150505b8051602080830191909120604080515f80825293810180835292909252601b908201526405ca1ab1e06060820152635ca1ab1e608082015260019060a0016020604051602081039080840390855afa15801561253b573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81166125b3576040517fcd16196600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040515f906125f89084906405ca1ab1e090635ca1ab1e90601b907fff0000000000000000000000000000000000000000000000000000000000000090602001614f8e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529450505050505b949350505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff16331461268b576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fa5b56b7906fd0a20e3f35120dd8343db1e12e037a6c90111c7e42885e82a1ce690602001610c3e565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314612754576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60036127608282614a7b565b507f6b8f723a4c7a5335cafae8a598a0aa0301be1387c037dccc085b62add6448b2081604051610c3e9190614259565b60025473ffffffffffffffffffffffffffffffffffffffff1633146127e1576040517f11e7be1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855f81900361281c576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e8811115612858576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612863602442614bf2565b8667ffffffffffffffff1611156128a6576040517f0a00feb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166379e2cf976040518163ffffffff1660e01b81526004015f604051808303815f87803b15801561290b575f80fd5b505af115801561291d573d5f803e3d5ffd5b505050505f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16635ca1e1656040518163ffffffff1660e01b8152600401602060405180830381865afa15801561298b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129af9190614848565b60075460055491925068010000000000000000900467ffffffffffffffff1690815f805b86811015612ce0575f8e8e838181106129ee576129ee614c05565b905060800201803603810190612a049190614fe9565b604081015190915067ffffffffffffffff1615612bf15785612a2581614cd9565b9650505f815f0151826020015183604001518460600151604051602001612a8a9493929190938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff8a165f90815260069093529120549091508114612b12576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85825f0151836020015184604001518f8660600151604051602001612bab969594939291909586526020860194909452604085019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166060808501919091521b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166068830152607c820152609c0190565b60405160208183030381529060405280519060200120955060065f8867ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f905550612ccd565b8051604051612c0d918591602001918252602082015260400190565b60405160208183030381529060405280519060200120925084815f0151888f8e5f801b604051602001612cb4969594939291909586526020860194909452604085019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166060808501919091521b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166068830152607c820152609c0190565b6040516020818303038152906040528051906020012094505b5080612cd881614cff565b9150506129d3565b5060075467ffffffffffffffff9081169085161115612d2b576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058390558567ffffffffffffffff85811690841614612de0575f612d50848761488c565b9050612d6667ffffffffffffffff821683614a02565b9150612d9f7f00000000000000000000000000000000000000000000000000000000000000008267ffffffffffffffff166120be610899565b50600780547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000067ffffffffffffffff8816021790555b8015612f6957612ee2337f0000000000000000000000000000000000000000000000000000000000000000837f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663477fa2706040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e75573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e999190614848565b612ea39190614d36565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169291906136de565b603c546040517f3b51be4b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690633b51be4b90612f3c9085908d908d9060040161507c565b5f6040518083038186803b158015612f52575f80fd5b505afa158015612f64573d5f803e3d5ffd5b505050505b6040517f9a908e7300000000000000000000000000000000000000000000000000000000815267ffffffffffffffff88166004820152602481018590525f907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af1158015613005573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130299190614a1b565b9050613035888261488c565b67ffffffffffffffff168c67ffffffffffffffff1614613081576040517f1a070d9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff167f3e54d0825ed78523037d00a81759237eb436ce774bd546993ee67a1b67b6e766886040516130bd91815260200190565b60405180910390a2505050505050505050505050505050565b603c5474010000000000000000000000000000000000000000900460ff1661312a576040517f821935b400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6131378585858585613742565b5050505050565b60085473ffffffffffffffffffffffffffffffffffffffff16801580159061317c575073ffffffffffffffffffffffffffffffffffffffff81163314155b156131b3576040517f24eff8c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa15801561321c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613240919061493d565b15613277576040517f39258d1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663604691696040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132e1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133059190614848565b905082811115613341576040517f2354600f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61138884111561337d576040517fa29a6c7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6133bf73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330846136de565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613429573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061344d9190614848565b6007805491925067ffffffffffffffff909116905f61346b83614cd9565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505085856040516134a2929190615095565b60405190819003902081426134b8600143614a02565b60408051602081019590955284019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166060830152406068820152608801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060075467ffffffffffffffff165f90815260069093529120553233036135ae576007546040805183815233602082015260608183018190525f90820152905167ffffffffffffffff909216917ff94bb37db835f1ab585ee00041849a09b12cd081d77fa15ca070757619cbc9319181900360800190a26135fd565b60075460405167ffffffffffffffff909116907ff94bb37db835f1ab585ee00041849a09b12cd081d77fa15ca070757619cbc931906135f490849033908b908b906150a4565b60405180910390a25b505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526136d99084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613f78565b505050565b60405173ffffffffffffffffffffffffffffffffffffffff8085166024830152831660448201526064810182905261373c9085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613657565b50505050565b60025473ffffffffffffffffffffffffffffffffffffffff163314613793576040517f11e7be1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835f8190036137ce576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e881111561380a576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613815602442614bf2565b8467ffffffffffffffff161115613858576040517f0a00feb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166379e2cf976040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156138bd575f80fd5b505af11580156138cf573d5f803e3d5ffd5b505050505f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16635ca1e1656040518163ffffffff1660e01b8152600401602060405180830381865afa15801561393d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139619190614848565b60075460055491925068010000000000000000900467ffffffffffffffff1690815f5b85811015613c7f575f8b8b8381811061399f5761399f614c05565b90506020028101906139b19190614c32565b6139ba90614c6e565b8051805160209091012060408201519192509067ffffffffffffffff1615613b9a57856139e681614cd9565b9650505f81836020015184604001518560600151604051602001613a489493929190938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff8a165f90815260069093529120549091508114613ad0576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208381015160408086015160608088015183519586018c90529285018790528481019390935260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166080840152908c901b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166088830152609c82015260bc0160405160208183030381529060405280519060200120955060065f8867ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f905550613c6a565b8151516201d4c01015613bd9576040517fa29a6c7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160208101879052908101829052606080820189905260c08d901b7fffffffffffffffff0000000000000000000000000000000000000000000000001660808301528a901b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660888201525f609c82015260bc016040516020818303038152906040528051906020012094505b50508080613c7790614cff565b915050613984565b5060075467ffffffffffffffff9081169084161115613cca576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058290558467ffffffffffffffff84811690831614613d7f575f613cef838661488c565b9050613d0567ffffffffffffffff821683614a02565b9150613d3e7f00000000000000000000000000000000000000000000000000000000000000008267ffffffffffffffff166120be610899565b50600780547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000067ffffffffffffffff8716021790555b613e0e337f0000000000000000000000000000000000000000000000000000000000000000837f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663477fa2706040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e75573d5f803e3d5ffd5b6040517f9a908e7300000000000000000000000000000000000000000000000000000000815267ffffffffffffffff87166004820152602481018490525f907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af1158015613eaa573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613ece9190614a1b565b9050613eda878261488c565b67ffffffffffffffff168967ffffffffffffffff1614613f26576040517f1a070d9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff167f3e54d0825ed78523037d00a81759237eb436ce774bd546993ee67a1b67b6e76687604051613f6291815260200190565b60405180910390a2505050505050505050505050565b5f613fd9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166140839092919063ffffffff16565b8051909150156136d95780806020019051810190613ff7919061493d565b6136d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610b42565b606061262d84845f85855f808673ffffffffffffffffffffffffffffffffffffffff1685876040516140b591906150e3565b5f6040518083038185875af1925050503d805f81146140ef576040519150601f19603f3d011682016040523d82523d5f602084013e6140f4565b606091505b509150915061410587838387614110565b979650505050505050565b606083156141a55782515f0361419e5773ffffffffffffffffffffffffffffffffffffffff85163b61419e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610b42565b508161262d565b61262d83838151156141ba5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b429190614259565b5f5b838110156142085781810151838201526020016141f0565b50505f910152565b5f81518084526142278160208601602086016141ee565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081525f61426b6020830184614210565b9392505050565b801515811461427f575f80fd5b50565b5f60208284031215614292575f80fd5b813561426b81614272565b67ffffffffffffffff8116811461427f575f80fd5b80356142bd8161429d565b919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461427f575f80fd5b80356142bd816142c2565b5f805f60608486031215614300575f80fd5b833561430b8161429d565b9250602084013591506040840135614322816142c2565b809150509250925092565b5f6020828403121561433d575f80fd5b813561426b8161429d565b5f60208284031215614358575f80fd5b813561426b816142c2565b63ffffffff8116811461427f575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040516080810167ffffffffffffffff811182821017156143c4576143c4614374565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561441157614411614374565b604052919050565b5f67ffffffffffffffff82111561443257614432614374565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b5f82601f83011261446d575f80fd5b813561448061447b82614419565b6143ca565b818152846020838601011115614494575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f8060c087890312156144c5575f80fd5b86356144d0816142c2565b955060208701356144e0816142c2565b945060408701356144f081614363565b93506060870135614500816142c2565b9250608087013567ffffffffffffffff8082111561451c575f80fd5b6145288a838b0161445e565b935060a089013591508082111561453d575f80fd5b5061454a89828a0161445e565b9150509295509295509295565b5f8083601f840112614567575f80fd5b50813567ffffffffffffffff81111561457e575f80fd5b6020830191508360208260051b8501011115614598575f80fd5b9250929050565b5f80602083850312156145b0575f80fd5b823567ffffffffffffffff8111156145c6575f80fd5b6145d285828601614557565b90969095509350505050565b5f805f80608085870312156145f1575f80fd5b84356145fc81614363565b9350602085013561460c816142c2565b9250604085013561461c81614363565b9150606085013567ffffffffffffffff811115614637575f80fd5b6146438782880161445e565b91505092959194509250565b5f6020828403121561465f575f80fd5b813567ffffffffffffffff811115614675575f80fd5b61262d8482850161445e565b5f8083601f840112614691575f80fd5b50813567ffffffffffffffff8111156146a8575f80fd5b602083019150836020828501011115614598575f80fd5b5f805f805f805f60a0888a0312156146d5575f80fd5b873567ffffffffffffffff808211156146ec575f80fd5b818a0191508a601f8301126146ff575f80fd5b81358181111561470d575f80fd5b8b60208260071b8501011115614721575f80fd5b6020830199508098505061473760208b016142b2565b965061474560408b016142b2565b955061475360608b016142e3565b945060808a0135915080821115614768575f80fd5b506147758a828b01614681565b989b979a50959850939692959293505050565b5f805f805f6080868803121561479c575f80fd5b853567ffffffffffffffff8111156147b2575f80fd5b6147be88828901614557565b90965094505060208601356147d28161429d565b925060408601356147e28161429d565b915060608601356147f2816142c2565b809150509295509295909350565b5f805f60408486031215614812575f80fd5b833567ffffffffffffffff811115614828575f80fd5b61483486828701614681565b909790965060209590950135949350505050565b5f60208284031215614858575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b67ffffffffffffffff8281168282160390808211156148ad576148ad61485f565b5092915050565b5f826148e7577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b600181811c9082168061490057607f821691505b602082108103614937577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b5f6020828403121561494d575f80fd5b815161426b81614272565b5f60208284031215614968575f80fd5b815167ffffffffffffffff81111561497e575f80fd5b8201601f8101841361498e575f80fd5b805161499c61447b82614419565b8181528560208385010111156149b0575f80fd5b6149c18260208301602086016141ee565b95945050505050565b5f80604083850312156149db575f80fd5b82516149e681614363565b60208401519092506149f7816142c2565b809150509250929050565b81810381811115614a1557614a1561485f565b92915050565b5f60208284031215614a2b575f80fd5b815161426b8161429d565b601f8211156136d9575f81815260208120601f850160051c81016020861015614a5c5750805b601f850160051c820191505b818110156135fd57828155600101614a68565b815167ffffffffffffffff811115614a9557614a95614374565b614aa981614aa384546148ec565b84614a36565b602080601f831160018114614afb575f8415614ac55750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556135fd565b5f858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015614b4757888601518255948401946001909101908401614b28565b5085821015614b8357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b606081525f614ba56060830186614210565b905083602083015273ffffffffffffffffffffffffffffffffffffffff83166040830152949350505050565b67ffffffffffffffff8181168382160190808211156148ad576148ad61485f565b80820180821115614a1557614a1561485f565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112614c64575f80fd5b9190910192915050565b5f60808236031215614c7e575f80fd5b614c866143a1565b823567ffffffffffffffff811115614c9c575f80fd5b614ca83682860161445e565b825250602083013560208201526040830135614cc38161429d565b6040820152606092830135928101929092525090565b5f67ffffffffffffffff808316818103614cf557614cf561485f565b6001019392505050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614d2f57614d2f61485f565b5060010190565b8082028115828204841417614a1557614a1561485f565b5f63ffffffff808916835273ffffffffffffffffffffffffffffffffffffffff8089166020850152818816604085015280871660608501528086166080850152505060c060a0830152614da360c0830184614210565b98975050505050505050565b61ffff8181168382160190808211156148ad576148ad61485f565b5f7fff00000000000000000000000000000000000000000000000000000000000000808a60f81b1683527fffff0000000000000000000000000000000000000000000000000000000000008960f01b1660018401528751614e32816003860160208c016141ee565b80840190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008860601b1660038201528651614e75816017840160208b016141ee565b808201915050818660f81b16601782015284519150614e9b8260188301602088016141ee565b016018019998505050505050505050565b7fff000000000000000000000000000000000000000000000000000000000000008860f81b1681525f7fffff000000000000000000000000000000000000000000000000000000000000808960f01b1660018401528751614f14816003860160208c016141ee565b80840190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008860601b1660038201528651614f57816017840160208b016141ee565b808201915050818660f01b16601782015284519150614f7d8260198301602088016141ee565b016019019998505050505050505050565b5f8651614f9f818460208b016141ee565b9190910194855250602084019290925260f81b7fff000000000000000000000000000000000000000000000000000000000000009081166040840152166041820152604201919050565b5f60808284031215614ff9575f80fd5b6150016143a1565b8235815260208301356020820152604083013561501d8161429d565b60408201526060928301359281019290925250919050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b838152604060208201525f6149c1604083018486615035565b818382375f9101908152919050565b84815273ffffffffffffffffffffffffffffffffffffffff84166020820152606060408201525f6150d9606083018486615035565b9695505050505050565b5f8251614c648184602087016141ee56fea26469706673582212204858b819e069d15c2eda851551489625889148d78d99fe49fd166f9ca8fd780064736f6c63430008140033000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f60000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab2
Deployed Bytecode
0x608060405234801561000f575f80fd5b5060043610610319575f3560e01c806371257022116101a8578063c7fffd4b116100f3578063def57e541161009e578063e7a7ed0211610079578063e7a7ed0214610845578063eaeb077b14610859578063f35dda471461086c578063f851a44014610874575f80fd5b8063def57e54146107eb578063e46761c4146107fe578063e57a0b4c14610825575f80fd5b8063d02103ca116100ce578063d02103ca146107a6578063d7bc90ff146107cd578063db5b0ed7146107d8575f80fd5b8063c7fffd4b1461076b578063c89e42df14610773578063cfa8ed4714610786575f80fd5b80639f26f84011610153578063ada8f9191161012e578063ada8f91914610724578063b0afe15414610737578063c754c7ed14610743575f80fd5b80639f26f840146106d7578063a3c573eb146106ea578063a652f26c14610711575f80fd5b80638c3d7301116101835780638c3d7301146106a157806391cafe32146106a95780639e001877146106bc575f80fd5b8063712570221461063f5780637a5460c5146106525780637cd76b8b1461068e575f80fd5b806340b5de6c1161026857806352bdeb6d116102135780636b8616ce116101ee5780636b8616ce146106045780636e05d2cd146106235780636ff512cc1461062c575f80fd5b806352bdeb6d146105b8578063542028d5146105f4578063676870d2146105fc575f80fd5b806349b7b8021161024357806349b7b802146105495780634c21fef3146105705780634e487706146105a5575f80fd5b806340b5de6c146104b057806342308fab146105085780634560526714610510575f80fd5b806326782247116102c857806332c2d153116102a357806332c2d153146104405780633c351e10146104535780633cbc795b14610473575f80fd5b806326782247146103c85780632acdc2b61461040d5780632c111c0614610420575f80fd5b8063107bf28c116102f8578063107bf28c1461039c57806311e892d4146103a45780631c8b9370146103be575f80fd5b8062d0295d1461031d578063035089631461033857806305835f3714610353575b5f80fd5b610325610899565b6040519081526020015b60405180910390f35b610340602081565b60405161ffff909116815260200161032f565b61038f6040518060400160405280600881526020017f80808401c9c3809400000000000000000000000000000000000000000000000081525081565b60405161032f9190614259565b61038f61099f565b6103ac60f981565b60405160ff909116815260200161032f565b6103c6610a2b565b005b6001546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161032f565b6103c661041b366004614282565b610c49565b6008546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b6103c661044e3660046142ee565b610d67565b6009546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b60095461049b9074010000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff909116815260200161032f565b6104d77fff0000000000000000000000000000000000000000000000000000000000000081565b6040517fff00000000000000000000000000000000000000000000000000000000000000909116815260200161032f565b610325602481565b6007546105309068010000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff909116815260200161032f565b6103e87f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab281565b603c546105959074010000000000000000000000000000000000000000900460ff1681565b604051901515815260200161032f565b6103c66105b336600461432d565b610e36565b61038f6040518060400160405280600281526020017f80b800000000000000000000000000000000000000000000000000000000000081525081565b61038f61103e565b610340601f81565b61032561061236600461432d565b60066020525f908152604090205481565b61032560055481565b6103c661063a366004614348565b61104b565b6103c661064d3660046144b0565b611114565b61038f6040518060400160405280600281526020017f80b900000000000000000000000000000000000000000000000000000000000081525081565b6103c661069c366004614348565b611918565b6103c66119e1565b6103c66106b7366004614348565b611ab3565b6103e873a40d5f56745a118d0906a34e69aec8c0db1cb8fa81565b6103c66106e536600461459f565b611bcb565b6103e87f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede81565b61038f61071f3660046145de565b612257565b6103c6610732366004614348565b612635565b6103256405ca1ab1e081565b60075461053090700100000000000000000000000000000000900467ffffffffffffffff1681565b6103ac60e481565b6103c661078136600461464f565b6126fe565b6002546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b6103e87f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb81565b610325635ca1ab1e81565b6103c66107e63660046146bf565b612790565b6103c66107f9366004614788565b6130d6565b6103e87f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f681565b603c546103e89073ffffffffffffffffffffffffffffffffffffffff1681565b6007546105309067ffffffffffffffff1681565b6103c6610867366004614800565b61313e565b6103ac601b81565b5f546103e89062010000900473ffffffffffffffffffffffffffffffffffffffff1681565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f90819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f616906370a0823190602401602060405180830381865afa158015610925573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109499190614848565b6007549091505f906109739067ffffffffffffffff6801000000000000000082048116911661488c565b67ffffffffffffffff169050805f0361098e575f9250505090565b61099881836148b4565b9250505090565b600480546109ac906148ec565b80601f01602080910402602001604051908101604052809291908181526020018280546109d8906148ec565b8015610a235780601f106109fa57610100808354040283529160200191610a23565b820191905f5260205f20905b815481529060010190602001808311610a0657829003601f168201915b505050505081565b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff163314610a9a576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54600290610100900460ff16158015610aba57505f5460ff8083169116105b610b4b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b5f8054600a8054603c80547401000000000000000000000000000000000000000080840460ff90811615159091027fffffffffffffffffffffff00000000000000000000000000000000000000000092831673ffffffffffffffffffffffffffffffffffffffff861617179092559091169091557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909116908316908117610100177fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a150565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314610c9f576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603c5474010000000000000000000000000000000000000000900460ff16151581151503610cf9576040517f5f0e7abe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603c80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000831515021790556040517ff32a0473f809a720a4f8af1e50d353f1caf7452030626fdaac4273f5e6587f41905f90a150565b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff163314610dd6576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff168367ffffffffffffffff167f9c72852172521097ba7e1482e6b44b351323df0155f97f4ea18fcec28e1f596684604051610e2991815260200190565b60405180910390a3505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314610e8c576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62093a8067ffffffffffffffff82161115610ed3576040517ff5e37f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f3c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f60919061493d565b610fc15760075467ffffffffffffffff700100000000000000000000000000000000909104811690821610610fc1576040517ff5e37f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000067ffffffffffffffff8416908102919091179091556040519081527fa7eb6cb8a613eb4e8bddc1ac3d61ec6cf10898760f0b187bcca794c6ca6fa40b90602001610c3e565b600380546109ac906148ec565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff1633146110a1576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527ff54144f9611984021529f814a1cb6a41e22c58351510a0d9f7e822618abb9cc090602001610c3e565b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff163314611183576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54610100900460ff16158080156111a157505f54600160ff909116105b806111ba5750303b1580156111ba57505f5460ff166001145b611246576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b42565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156112a2575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b606073ffffffffffffffffffffffffffffffffffffffff8516156114ff576040517fc00f14ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301527f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede169063c00f14ab906024015f60405180830381865afa158015611349573d5f803e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261138e9190810190614958565b6040517f318aee3d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87811660048301529192505f9182917f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede9091169063318aee3d906024016040805180830381865afa158015611420573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061144491906149ca565b915091508163ffffffff165f146114bb576009805463ffffffff841674010000000000000000000000000000000000000000027fffffffffffffffff00000000000000000000000000000000000000000000000090911673ffffffffffffffffffffffffffffffffffffffff8416171790556114fc565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff89161790555b50505b6009545f9061154690889073ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900463ffffffff1685612257565b90505f818051906020012090505f4290505f7f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb73ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115c1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115e59190614848565b90505f808483858f6115f8600143614a02565b60408051602081019790975286019490945260608086019390935260c09190911b7fffffffffffffffff000000000000000000000000000000000000000000000000166080850152901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016608883015240609c82015260bc01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815290829052805160209091012060058190557f9a908e73000000000000000000000000000000000000000000000000000000008252600160048301526024820181905291507f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af115801561173e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117629190614a1b565b508c5f60026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508b60025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555088600390816117f29190614a7b565b5060046117ff8982614a7b565b508c60085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062069780600760106101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507f060116213bcbf54ca19fd649dc84b59ab2bbd200ab199770e4d923e222a28e7f85838e60405161189f93929190614b93565b60405180910390a1505050505050801561190f575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff16331461196e576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603c80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fd331bd4c4cd1afecb94a225184bded161ff3213624ba4fb58c4f30c5a861144a90602001610c3e565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a32576040517fd1ec4b2300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001545f80547fffffffffffffffffffff0000000000000000000000000000000000000000ffff1673ffffffffffffffffffffffffffffffffffffffff9092166201000081029290921790556040519081527f056dc487bbf0795d0bbb1b4f0af523a855503cff740bfb4d5475f7a90c091e8e9060200160405180910390a1565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314611b09576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60085473ffffffffffffffffffffffffffffffffffffffff16611b58576040517fc89374d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f5fbd7dd171301c4a1611a84aac4ba86d119478560557755f7927595b082634fb90602001610c3e565b60085473ffffffffffffffffffffffffffffffffffffffff168015801590611c09575073ffffffffffffffffffffffffffffffffffffffff81163314155b15611c40576040517f24eff8c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4262093a807f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff166330c27dde6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cd29190614a1b565b611cdc9190614bd1565b67ffffffffffffffff161115611d1e576040517f3d49ed4c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f819003611d59576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e8811115611d95576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60075467ffffffffffffffff80821691611dbd91849168010000000000000000900416614bf2565b1115611df5576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007546005546801000000000000000090910467ffffffffffffffff16905f5b83811015612090575f878783818110611e3057611e30614c05565b9050602002810190611e429190614c32565b611e4b90614c6e565b905083611e5781614cd9565b825180516020918201208185015160408087015160608801519151959a509295505f94611ec3948794929101938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff89165f90815260069093529120549091508114611f4b576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff86165f90815260066020526040812055611f6f600188614a02565b8403611fde5742600760109054906101000a900467ffffffffffffffff168460400151611f9c9190614bd1565b67ffffffffffffffff161115611fde576040517fc44a082100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208381015160408086015160608088015183519586018b90529285018790528481019390935260c01b7fffffffffffffffff0000000000000000000000000000000000000000000000001660808401523390911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166088830152609c82015260bc01604051602081830303815290604052805190602001209450505050808061208890614cff565b915050611e15565b506121067f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab2846120be610899565b6120c89190614d36565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f6169190613605565b60058190556007805467ffffffffffffffff841668010000000000000000027fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff9091161790556040517f9a908e730000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab21690639a908e73906121d7908790869060040167ffffffffffffffff929092168252602082015260400190565b6020604051808303815f875af11580156121f3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122179190614a1b565b60405190915067ffffffffffffffff8216907f648a61dd2438f072f5a1960939abd30f37aea80d2e94c9792ad142d3e0a490a4905f90a250505050505050565b60605f85858573a40d5f56745a118d0906a34e69aec8c0db1cb8fa5f8760405160240161228996959493929190614d4d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167ff811bff70000000000000000000000000000000000000000000000000000000017905283519091506060905f036123d95760f9601f835161231d9190614daf565b6040518060400160405280600881526020017f80808401c9c380940000000000000000000000000000000000000000000000008152507f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede6040518060400160405280600281526020017f80b800000000000000000000000000000000000000000000000000000000000081525060e4876040516020016123c39796959493929190614dca565b60405160208183030381529060405290506124dd565b815161ffff1015612416576040517f248b8f8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815160f9612425602083614daf565b6040518060400160405280600881526020017f80808401c9c380940000000000000000000000000000000000000000000000008152507f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede6040518060400160405280600281526020017f80b900000000000000000000000000000000000000000000000000000000000081525085886040516020016124ca9796959493929190614eac565b6040516020818303038152906040529150505b8051602080830191909120604080515f80825293810180835292909252601b908201526405ca1ab1e06060820152635ca1ab1e608082015260019060a0016020604051602081039080840390855afa15801561253b573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81166125b3576040517fcd16196600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040515f906125f89084906405ca1ab1e090635ca1ab1e90601b907fff0000000000000000000000000000000000000000000000000000000000000090602001614f8e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529450505050505b949350505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff16331461268b576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fa5b56b7906fd0a20e3f35120dd8343db1e12e037a6c90111c7e42885e82a1ce690602001610c3e565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314612754576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60036127608282614a7b565b507f6b8f723a4c7a5335cafae8a598a0aa0301be1387c037dccc085b62add6448b2081604051610c3e9190614259565b60025473ffffffffffffffffffffffffffffffffffffffff1633146127e1576040517f11e7be1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855f81900361281c576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e8811115612858576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612863602442614bf2565b8667ffffffffffffffff1611156128a6576040517f0a00feb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede73ffffffffffffffffffffffffffffffffffffffff166379e2cf976040518163ffffffff1660e01b81526004015f604051808303815f87803b15801561290b575f80fd5b505af115801561291d573d5f803e3d5ffd5b505050505f7f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb73ffffffffffffffffffffffffffffffffffffffff16635ca1e1656040518163ffffffff1660e01b8152600401602060405180830381865afa15801561298b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129af9190614848565b60075460055491925068010000000000000000900467ffffffffffffffff1690815f805b86811015612ce0575f8e8e838181106129ee576129ee614c05565b905060800201803603810190612a049190614fe9565b604081015190915067ffffffffffffffff1615612bf15785612a2581614cd9565b9650505f815f0151826020015183604001518460600151604051602001612a8a9493929190938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff8a165f90815260069093529120549091508114612b12576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85825f0151836020015184604001518f8660600151604051602001612bab969594939291909586526020860194909452604085019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166060808501919091521b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166068830152607c820152609c0190565b60405160208183030381529060405280519060200120955060065f8867ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f905550612ccd565b8051604051612c0d918591602001918252602082015260400190565b60405160208183030381529060405280519060200120925084815f0151888f8e5f801b604051602001612cb4969594939291909586526020860194909452604085019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166060808501919091521b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166068830152607c820152609c0190565b6040516020818303038152906040528051906020012094505b5080612cd881614cff565b9150506129d3565b5060075467ffffffffffffffff9081169085161115612d2b576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058390558567ffffffffffffffff85811690841614612de0575f612d50848761488c565b9050612d6667ffffffffffffffff821683614a02565b9150612d9f7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab28267ffffffffffffffff166120be610899565b50600780547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000067ffffffffffffffff8816021790555b8015612f6957612ee2337f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab2837f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1663477fa2706040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e75573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e999190614848565b612ea39190614d36565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f6169291906136de565b603c546040517f3b51be4b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690633b51be4b90612f3c9085908d908d9060040161507c565b5f6040518083038186803b158015612f52575f80fd5b505afa158015612f64573d5f803e3d5ffd5b505050505b6040517f9a908e7300000000000000000000000000000000000000000000000000000000815267ffffffffffffffff88166004820152602481018590525f907f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af1158015613005573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130299190614a1b565b9050613035888261488c565b67ffffffffffffffff168c67ffffffffffffffff1614613081576040517f1a070d9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff167f3e54d0825ed78523037d00a81759237eb436ce774bd546993ee67a1b67b6e766886040516130bd91815260200190565b60405180910390a2505050505050505050505050505050565b603c5474010000000000000000000000000000000000000000900460ff1661312a576040517f821935b400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6131378585858585613742565b5050505050565b60085473ffffffffffffffffffffffffffffffffffffffff16801580159061317c575073ffffffffffffffffffffffffffffffffffffffff81163314155b156131b3576040517f24eff8c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa15801561321c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613240919061493d565b15613277576040517f39258d1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1663604691696040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132e1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133059190614848565b905082811115613341576040517f2354600f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61138884111561337d576040517fa29a6c7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6133bf73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f6163330846136de565b5f7f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb73ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613429573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061344d9190614848565b6007805491925067ffffffffffffffff909116905f61346b83614cd9565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505085856040516134a2929190615095565b60405190819003902081426134b8600143614a02565b60408051602081019590955284019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166060830152406068820152608801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060075467ffffffffffffffff165f90815260069093529120553233036135ae576007546040805183815233602082015260608183018190525f90820152905167ffffffffffffffff909216917ff94bb37db835f1ab585ee00041849a09b12cd081d77fa15ca070757619cbc9319181900360800190a26135fd565b60075460405167ffffffffffffffff909116907ff94bb37db835f1ab585ee00041849a09b12cd081d77fa15ca070757619cbc931906135f490849033908b908b906150a4565b60405180910390a25b505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526136d99084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613f78565b505050565b60405173ffffffffffffffffffffffffffffffffffffffff8085166024830152831660448201526064810182905261373c9085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613657565b50505050565b60025473ffffffffffffffffffffffffffffffffffffffff163314613793576040517f11e7be1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835f8190036137ce576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e881111561380a576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613815602442614bf2565b8467ffffffffffffffff161115613858576040517f0a00feb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede73ffffffffffffffffffffffffffffffffffffffff166379e2cf976040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156138bd575f80fd5b505af11580156138cf573d5f803e3d5ffd5b505050505f7f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb73ffffffffffffffffffffffffffffffffffffffff16635ca1e1656040518163ffffffff1660e01b8152600401602060405180830381865afa15801561393d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139619190614848565b60075460055491925068010000000000000000900467ffffffffffffffff1690815f5b85811015613c7f575f8b8b8381811061399f5761399f614c05565b90506020028101906139b19190614c32565b6139ba90614c6e565b8051805160209091012060408201519192509067ffffffffffffffff1615613b9a57856139e681614cd9565b9650505f81836020015184604001518560600151604051602001613a489493929190938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff8a165f90815260069093529120549091508114613ad0576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208381015160408086015160608088015183519586018c90529285018790528481019390935260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166080840152908c901b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166088830152609c82015260bc0160405160208183030381529060405280519060200120955060065f8867ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f905550613c6a565b8151516201d4c01015613bd9576040517fa29a6c7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160208101879052908101829052606080820189905260c08d901b7fffffffffffffffff0000000000000000000000000000000000000000000000001660808301528a901b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660888201525f609c82015260bc016040516020818303038152906040528051906020012094505b50508080613c7790614cff565b915050613984565b5060075467ffffffffffffffff9081169084161115613cca576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058290558467ffffffffffffffff84811690831614613d7f575f613cef838661488c565b9050613d0567ffffffffffffffff821683614a02565b9150613d3e7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab28267ffffffffffffffff166120be610899565b50600780547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000067ffffffffffffffff8716021790555b613e0e337f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab2837f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1663477fa2706040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e75573d5f803e3d5ffd5b6040517f9a908e7300000000000000000000000000000000000000000000000000000000815267ffffffffffffffff87166004820152602481018490525f907f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af1158015613eaa573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613ece9190614a1b565b9050613eda878261488c565b67ffffffffffffffff168967ffffffffffffffff1614613f26576040517f1a070d9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff167f3e54d0825ed78523037d00a81759237eb436ce774bd546993ee67a1b67b6e76687604051613f6291815260200190565b60405180910390a2505050505050505050505050565b5f613fd9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166140839092919063ffffffff16565b8051909150156136d95780806020019051810190613ff7919061493d565b6136d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610b42565b606061262d84845f85855f808673ffffffffffffffffffffffffffffffffffffffff1685876040516140b591906150e3565b5f6040518083038185875af1925050503d805f81146140ef576040519150601f19603f3d011682016040523d82523d5f602084013e6140f4565b606091505b509150915061410587838387614110565b979650505050505050565b606083156141a55782515f0361419e5773ffffffffffffffffffffffffffffffffffffffff85163b61419e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610b42565b508161262d565b61262d83838151156141ba5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b429190614259565b5f5b838110156142085781810151838201526020016141f0565b50505f910152565b5f81518084526142278160208601602086016141ee565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081525f61426b6020830184614210565b9392505050565b801515811461427f575f80fd5b50565b5f60208284031215614292575f80fd5b813561426b81614272565b67ffffffffffffffff8116811461427f575f80fd5b80356142bd8161429d565b919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461427f575f80fd5b80356142bd816142c2565b5f805f60608486031215614300575f80fd5b833561430b8161429d565b9250602084013591506040840135614322816142c2565b809150509250925092565b5f6020828403121561433d575f80fd5b813561426b8161429d565b5f60208284031215614358575f80fd5b813561426b816142c2565b63ffffffff8116811461427f575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040516080810167ffffffffffffffff811182821017156143c4576143c4614374565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561441157614411614374565b604052919050565b5f67ffffffffffffffff82111561443257614432614374565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b5f82601f83011261446d575f80fd5b813561448061447b82614419565b6143ca565b818152846020838601011115614494575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f8060c087890312156144c5575f80fd5b86356144d0816142c2565b955060208701356144e0816142c2565b945060408701356144f081614363565b93506060870135614500816142c2565b9250608087013567ffffffffffffffff8082111561451c575f80fd5b6145288a838b0161445e565b935060a089013591508082111561453d575f80fd5b5061454a89828a0161445e565b9150509295509295509295565b5f8083601f840112614567575f80fd5b50813567ffffffffffffffff81111561457e575f80fd5b6020830191508360208260051b8501011115614598575f80fd5b9250929050565b5f80602083850312156145b0575f80fd5b823567ffffffffffffffff8111156145c6575f80fd5b6145d285828601614557565b90969095509350505050565b5f805f80608085870312156145f1575f80fd5b84356145fc81614363565b9350602085013561460c816142c2565b9250604085013561461c81614363565b9150606085013567ffffffffffffffff811115614637575f80fd5b6146438782880161445e565b91505092959194509250565b5f6020828403121561465f575f80fd5b813567ffffffffffffffff811115614675575f80fd5b61262d8482850161445e565b5f8083601f840112614691575f80fd5b50813567ffffffffffffffff8111156146a8575f80fd5b602083019150836020828501011115614598575f80fd5b5f805f805f805f60a0888a0312156146d5575f80fd5b873567ffffffffffffffff808211156146ec575f80fd5b818a0191508a601f8301126146ff575f80fd5b81358181111561470d575f80fd5b8b60208260071b8501011115614721575f80fd5b6020830199508098505061473760208b016142b2565b965061474560408b016142b2565b955061475360608b016142e3565b945060808a0135915080821115614768575f80fd5b506147758a828b01614681565b989b979a50959850939692959293505050565b5f805f805f6080868803121561479c575f80fd5b853567ffffffffffffffff8111156147b2575f80fd5b6147be88828901614557565b90965094505060208601356147d28161429d565b925060408601356147e28161429d565b915060608601356147f2816142c2565b809150509295509295909350565b5f805f60408486031215614812575f80fd5b833567ffffffffffffffff811115614828575f80fd5b61483486828701614681565b909790965060209590950135949350505050565b5f60208284031215614858575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b67ffffffffffffffff8281168282160390808211156148ad576148ad61485f565b5092915050565b5f826148e7577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b600181811c9082168061490057607f821691505b602082108103614937577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b5f6020828403121561494d575f80fd5b815161426b81614272565b5f60208284031215614968575f80fd5b815167ffffffffffffffff81111561497e575f80fd5b8201601f8101841361498e575f80fd5b805161499c61447b82614419565b8181528560208385010111156149b0575f80fd5b6149c18260208301602086016141ee565b95945050505050565b5f80604083850312156149db575f80fd5b82516149e681614363565b60208401519092506149f7816142c2565b809150509250929050565b81810381811115614a1557614a1561485f565b92915050565b5f60208284031215614a2b575f80fd5b815161426b8161429d565b601f8211156136d9575f81815260208120601f850160051c81016020861015614a5c5750805b601f850160051c820191505b818110156135fd57828155600101614a68565b815167ffffffffffffffff811115614a9557614a95614374565b614aa981614aa384546148ec565b84614a36565b602080601f831160018114614afb575f8415614ac55750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556135fd565b5f858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015614b4757888601518255948401946001909101908401614b28565b5085821015614b8357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b606081525f614ba56060830186614210565b905083602083015273ffffffffffffffffffffffffffffffffffffffff83166040830152949350505050565b67ffffffffffffffff8181168382160190808211156148ad576148ad61485f565b80820180821115614a1557614a1561485f565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112614c64575f80fd5b9190910192915050565b5f60808236031215614c7e575f80fd5b614c866143a1565b823567ffffffffffffffff811115614c9c575f80fd5b614ca83682860161445e565b825250602083013560208201526040830135614cc38161429d565b6040820152606092830135928101929092525090565b5f67ffffffffffffffff808316818103614cf557614cf561485f565b6001019392505050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614d2f57614d2f61485f565b5060010190565b8082028115828204841417614a1557614a1561485f565b5f63ffffffff808916835273ffffffffffffffffffffffffffffffffffffffff8089166020850152818816604085015280871660608501528086166080850152505060c060a0830152614da360c0830184614210565b98975050505050505050565b61ffff8181168382160190808211156148ad576148ad61485f565b5f7fff00000000000000000000000000000000000000000000000000000000000000808a60f81b1683527fffff0000000000000000000000000000000000000000000000000000000000008960f01b1660018401528751614e32816003860160208c016141ee565b80840190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008860601b1660038201528651614e75816017840160208b016141ee565b808201915050818660f81b16601782015284519150614e9b8260188301602088016141ee565b016018019998505050505050505050565b7fff000000000000000000000000000000000000000000000000000000000000008860f81b1681525f7fffff000000000000000000000000000000000000000000000000000000000000808960f01b1660018401528751614f14816003860160208c016141ee565b80840190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008860601b1660038201528651614f57816017840160208b016141ee565b808201915050818660f01b16601782015284519150614f7d8260198301602088016141ee565b016019019998505050505050505050565b5f8651614f9f818460208b016141ee565b9190910194855250602084019290925260f81b7fff000000000000000000000000000000000000000000000000000000000000009081166040840152166041820152604201919050565b5f60808284031215614ff9575f80fd5b6150016143a1565b8235815260208301356020820152604083013561501d8161429d565b60408201526060928301359281019290925250919050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b838152604060208201525f6149c1604083018486615035565b818382375f9101908152919050565b84815273ffffffffffffffffffffffffffffffffffffffff84166020820152606060408201525f6150d9606083018486615035565b9695505050505050565b5f8251614c648184602087016141ee56fea26469706673582212204858b819e069d15c2eda851551489625889148d78d99fe49fd166f9ca8fd780064736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f60000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab2
-----Decoded View---------------
Arg [0] : _globalExitRootManager (address): 0x580bda1e7A0CFAe92Fa7F6c20A3794F169CE3CFb
Arg [1] : _pol (address): 0x455e53CBB86018Ac2B8092FdCd39d8444aFFC3F6
Arg [2] : _bridgeAddress (address): 0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe
Arg [3] : _rollupManager (address): 0x5132A183E9F3CB7C848b0AAC5Ae0c4f0491B7aB2
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb
Arg [1] : 000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f6
Arg [2] : 0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede
Arg [3] : 0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab2
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.