Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
PolygonZkEVMExistentEtrog
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 "../../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: 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;
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;
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();
}// 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.
*/
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
);
// 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 l2Coinbase Address that will receive the fees from L2+
* note Pol is not a reentrant token
*/
function sequenceBatches(
BatchData[] calldata batches,
address l2Coinbase
) public virtual onlyTrustedSequencer {
uint256 batchesNum = batches.length;
if (batchesNum == 0) {
revert SequenceZeroBatches();
}
if (batchesNum > _MAX_VERIFY_BATCHES) {
revert ExceedMaxVerifyBatches();
}
// Update global exit root if there are new deposits
bridgeAddress.updateGlobalExitRoot();
// Get global batch variables
bytes32 l1InfoRoot = globalExitRootManager.getRoot();
uint64 currentTimestamp = uint64(block.timestamp);
// 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,
currentTimestamp,
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
);
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
Contract ABI
API[{"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":"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":"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":"SequenceZeroBatches","type":"error"},{"inputs":[],"name":"SequencedTimestampBelowForcedTimestamp","type":"error"},{"inputs":[],"name":"SequencedTimestampInvalid","type":"error"},{"inputs":[],"name":"StoredRootMustBeDifferentThanNewRoot","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":"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":[{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"TransferAdminRole","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"numBatch","type":"uint64"},{"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":"UpdateEtrogSequence","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":"SET_UP_ETROG_TX","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"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":"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":[{"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":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_trustedSequencer","type":"address"},{"internalType":"string","name":"_trustedSequencerURL","type":"string"},{"internalType":"string","name":"_networkName","type":"string"},{"internalType":"bytes32","name":"_lastAccInputHash","type":"bytes32"}],"name":"initializeUpgrade","outputs":[],"stateMutability":"nonpayable","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 PolygonRollupBaseEtrog.BatchData[]","name":"batches","type":"tuple[]"},{"internalType":"address","name":"l2Coinbase","type":"address"}],"name":"sequenceBatches","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 PolygonRollupBaseEtrog.BatchData[]","name":"batches","type":"tuple[]"}],"name":"sequenceForceBatches","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":"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
61010060405234801562000011575f80fd5b506040516200495f3803806200495f83398101604081905262000034916200006f565b6001600160a01b0393841660a052918316608052821660c0521660e052620000d4565b6001600160a01b03811681146200006c575f80fd5b50565b5f805f806080858703121562000083575f80fd5b8451620000908162000057565b6020860151909450620000a38162000057565b6040860151909350620000b68162000057565b6060860151909250620000c98162000057565b939692955090935050565b60805160a05160c05160e051614793620001cc5f395f81816104f40152818161097301528181610adf01528181610c5e01528181610fa8015281816112aa0152818161187701528181611d120152818161216101528181612256015281816128ee015281816129b3015281816132bc0152818161333501528181613357015261346c01525f8181610660015281816114990152818161156e01528181612420015281816125280152612e0701525f818161072401528181610e1d015281816116ee01528181612afb0152612e8301525f818161075601528181610826015281816121aa01528181612ad0015261340301526147935ff3fe608060405234801561000f575f80fd5b50600436106102e2575f3560e01c80637a5460c511610187578063c7fffd4b116100dd578063e46761c411610093578063ecef3f991161006e578063ecef3f991461079f578063f35dda47146107b2578063f851a440146107ba575f80fd5b8063e46761c414610751578063e7a7ed0214610778578063eaeb077b1461078c575f80fd5b8063cfa8ed47116100c3578063cfa8ed47146106ff578063d02103ca1461071f578063d7bc90ff14610746575f80fd5b8063c7fffd4b146106e4578063c89e42df146106ec575f80fd5b8063a3c573eb1161013d578063af7f3e0211610118578063af7f3e02146106a8578063b0afe154146106b0578063c754c7ed146106bc575f80fd5b8063a3c573eb1461065b578063a652f26c14610682578063ada8f91914610695575f80fd5b806391cafe321161016d57806391cafe321461061a5780639e0018771461062d5780639f26f84014610648575f80fd5b80637a5460c5146105d65780638c3d730114610612575f80fd5b8063456052671161023c5780635d6717a5116101f25780636e05d2cd116101cd5780636e05d2cd146105a75780636ff512cc146105b057806371257022146105c3575f80fd5b80635d6717a51461056d578063676870d2146105805780636b8616ce14610588575f80fd5b80634e487706116102225780634e4877061461051657806352bdeb6d14610529578063542028d514610565575f80fd5b806345605267146104b657806349b7b802146104ef575f80fd5b8063267822471161029c5780633c351e10116102775780633c351e10146104015780633cbc795b1461042157806340b5de6c1461045e575f80fd5b806326782247146103875780632c111c06146103cc57806332c2d153146103ec575f80fd5b806305835f37116102cc57806305835f371461031c578063107bf28c1461036557806311e892d41461036d575f80fd5b8062d0295d146102e65780630350896314610301575b5f80fd5b6102ee6107df565b6040519081526020015b60405180910390f35b610309602081565b60405161ffff90911681526020016102f8565b6103586040518060400160405280600881526020017f80808401c9c3809400000000000000000000000000000000000000000000000081525081565b6040516102f8919061395c565b6103586108e5565b61037560f981565b60405160ff90911681526020016102f8565b6001546103a79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102f8565b6008546103a79073ffffffffffffffffffffffffffffffffffffffff1681565b6103ff6103fa3660046139ae565b610971565b005b6009546103a79073ffffffffffffffffffffffffffffffffffffffff1681565b6009546104499074010000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016102f8565b6104857fff0000000000000000000000000000000000000000000000000000000000000081565b6040517fff0000000000000000000000000000000000000000000000000000000000000090911681526020016102f8565b6007546104d69068010000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016102f8565b6103a77f000000000000000000000000000000000000000000000000000000000000000081565b6103ff6105243660046139ed565b610a40565b6103586040518060400160405280600281526020017f80b800000000000000000000000000000000000000000000000000000000000081525081565b610358610c4f565b6103ff61057b366004613b1b565b610c5c565b610309601f81565b6102ee6105963660046139ed565b60066020525f908152604090205481565b6102ee60055481565b6103ff6105be366004613ba6565b6111df565b6103ff6105d1366004613bd2565b6112a8565b6103586040518060400160405280600281526020017f80b900000000000000000000000000000000000000000000000000000000000081525081565b6103ff611aac565b6103ff610628366004613ba6565b611b7e565b6103a773a40d5f56745a118d0906a34e69aec8c0db1cb8fa81565b6103ff610656366004613cc1565b611c96565b6103a77f000000000000000000000000000000000000000000000000000000000000000081565b610358610690366004613d00565b612322565b6103ff6106a3366004613ba6565b612700565b6103586127c9565b6102ee6405ca1ab1e081565b6007546104d690700100000000000000000000000000000000900467ffffffffffffffff1681565b61037560e481565b6103ff6106fa366004613d71565b6127e5565b6002546103a79073ffffffffffffffffffffffffffffffffffffffff1681565b6103a77f000000000000000000000000000000000000000000000000000000000000000081565b6102ee635ca1ab1e81565b6103a77f000000000000000000000000000000000000000000000000000000000000000081565b6007546104d69067ffffffffffffffff1681565b6103ff61079a366004613da3565b612877565b6103ff6107ad366004613e14565b612d3d565b610375601b81565b5f546103a79062010000900473ffffffffffffffffffffffffffffffffffffffff1681565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f90819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561086b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061088f9190613e5c565b6007549091505f906108b99067ffffffffffffffff68010000000000000000820481169116613ea0565b67ffffffffffffffff169050805f036108d4575f9250505090565b6108de8183613ec8565b9250505090565b600480546108f290613f00565b80601f016020809104026020016040519081016040528092919081815260200182805461091e90613f00565b80156109695780601f1061094057610100808354040283529160200191610969565b820191905f5260205f20905b81548152906001019060200180831161094c57829003601f168201915b505050505081565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1633146109e0576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff168367ffffffffffffffff167f9c72852172521097ba7e1482e6b44b351323df0155f97f4ea18fcec28e1f596684604051610a3391815260200190565b60405180910390a3505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314610a96576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62093a8067ffffffffffffffff82161115610add576040517ff5e37f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b46573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b6a9190613f51565b610bcb5760075467ffffffffffffffff700100000000000000000000000000000000909104811690821610610bcb576040517ff5e37f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000067ffffffffffffffff8416908102919091179091556040519081527fa7eb6cb8a613eb4e8bddc1ac3d61ec6cf10898760f0b187bcca794c6ca6fa40b906020015b60405180910390a150565b600380546108f290613f00565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163314610ccb576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54610100900460ff1615808015610ce957505f54600160ff909116105b80610d025750303b158015610d0257505f5460ff166001145b610d93576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610def575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b5f6040518060a00160405280606281526020016146fc6062913990505f818051906020012090505f4290505f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ea89190613e5c565b90505f868483858d610ebb600143613f70565b60408051602081019790975286019490945260608086019390935260c09190911b7fffffffffffffffff000000000000000000000000000000000000000000000000166080850152901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016608883015240609c82015260bc01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815290829052805160209091012060058190557f9a908e73000000000000000000000000000000000000000000000000000000008252600160048301526024820181905291505f907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af1158015611003573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110279190613f89565b90508b5f60026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508a60025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555089600390816110b89190613fe9565b5060046110c58a82613fe9565b508b60085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062069780600760106101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507fd2c80353fc15ef62c6affc7cd6b7ab5b42c43290c50be3372e55ae552cecd19c8187858e6040516111679493929190614101565b60405180910390a150505050505080156111d7575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314611235576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527ff54144f9611984021529f814a1cb6a41e22c58351510a0d9f7e822618abb9cc090602001610c44565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163314611317576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54610100900460ff161580801561133557505f54600160ff909116105b8061134e5750303b15801561134e57505f5460ff166001145b6113da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610d8a565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611436575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b606073ffffffffffffffffffffffffffffffffffffffff851615611693576040517fc00f14ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063c00f14ab906024015f60405180830381865afa1580156114dd573d5f803e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526115229190810190614150565b6040517f318aee3d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87811660048301529192505f9182917f00000000000000000000000000000000000000000000000000000000000000009091169063318aee3d906024016040805180830381865afa1580156115b4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115d891906141c2565b915091508163ffffffff165f1461164f576009805463ffffffff841674010000000000000000000000000000000000000000027fffffffffffffffff00000000000000000000000000000000000000000000000090911673ffffffffffffffffffffffffffffffffffffffff841617179055611690565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff89161790555b50505b6009545f906116da90889073ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900463ffffffff1685612322565b90505f818051906020012090505f4290505f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611755573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117799190613e5c565b90505f808483858f61178c600143613f70565b60408051602081019790975286019490945260608086019390935260c09190911b7fffffffffffffffff000000000000000000000000000000000000000000000000166080850152901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016608883015240609c82015260bc01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815290829052805160209091012060058190557f9a908e73000000000000000000000000000000000000000000000000000000008252600160048301526024820181905291507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af11580156118d2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118f69190613f89565b508c5f60026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508b60025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555088600390816119869190613fe9565b5060046119938982613fe9565b508c60085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062069780600760106101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507f060116213bcbf54ca19fd649dc84b59ab2bbd200ab199770e4d923e222a28e7f85838e604051611a33939291906141fa565b60405180910390a15050505050508015611aa3575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611afd576040517fd1ec4b2300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001545f80547fffffffffffffffffffff0000000000000000000000000000000000000000ffff1673ffffffffffffffffffffffffffffffffffffffff9092166201000081029290921790556040519081527f056dc487bbf0795d0bbb1b4f0af523a855503cff740bfb4d5475f7a90c091e8e9060200160405180910390a1565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314611bd4576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60085473ffffffffffffffffffffffffffffffffffffffff16611c23576040517fc89374d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f5fbd7dd171301c4a1611a84aac4ba86d119478560557755f7927595b082634fb90602001610c44565b60085473ffffffffffffffffffffffffffffffffffffffff168015801590611cd4575073ffffffffffffffffffffffffffffffffffffffff81163314155b15611d0b576040517f24eff8c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4262093a807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166330c27dde6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d79573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d9d9190613f89565b611da79190614238565b67ffffffffffffffff161115611de9576040517f3d49ed4c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f819003611e24576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e8811115611e60576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60075467ffffffffffffffff80821691611e8891849168010000000000000000900416614259565b1115611ec0576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007546005546801000000000000000090910467ffffffffffffffff16905f5b8381101561215b575f878783818110611efb57611efb61426c565b9050602002810190611f0d9190614299565b611f16906142d5565b905083611f228161435b565b825180516020918201208185015160408087015160608801519151959a509295505f94611f8e948794929101938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff89165f90815260069093529120549091508114612016576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff86165f9081526006602052604081205561203a600188613f70565b84036120a95742600760109054906101000a900467ffffffffffffffff1684604001516120679190614238565b67ffffffffffffffff1611156120a9576040517fc44a082100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208381015160408086015160608088015183519586018b90529285018790528481019390935260c01b7fffffffffffffffff0000000000000000000000000000000000000000000000001660808401523390911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166088830152609c82015260bc01604051602081830303815290604052805190602001209450505050808061215390614381565b915050611ee0565b506121d17f0000000000000000000000000000000000000000000000000000000000000000846121896107df565b61219391906143b8565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016919061353e565b60058190556007805467ffffffffffffffff841668010000000000000000027fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff9091161790556040517f9a908e730000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690639a908e73906122a2908790869060040167ffffffffffffffff929092168252602082015260400190565b6020604051808303815f875af11580156122be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e29190613f89565b60405190915067ffffffffffffffff8216907f648a61dd2438f072f5a1960939abd30f37aea80d2e94c9792ad142d3e0a490a4905f90a250505050505050565b60605f85858573a40d5f56745a118d0906a34e69aec8c0db1cb8fa5f87604051602401612354969594939291906143cf565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167ff811bff70000000000000000000000000000000000000000000000000000000017905283519091506060905f036124a45760f9601f83516123e89190614431565b6040518060400160405280600881526020017f80808401c9c380940000000000000000000000000000000000000000000000008152507f00000000000000000000000000000000000000000000000000000000000000006040518060400160405280600281526020017f80b800000000000000000000000000000000000000000000000000000000000081525060e48760405160200161248e979695949392919061444c565b60405160208183030381529060405290506125a8565b815161ffff10156124e1576040517f248b8f8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815160f96124f0602083614431565b6040518060400160405280600881526020017f80808401c9c380940000000000000000000000000000000000000000000000008152507f00000000000000000000000000000000000000000000000000000000000000006040518060400160405280600281526020017f80b90000000000000000000000000000000000000000000000000000000000008152508588604051602001612595979695949392919061452e565b6040516020818303038152906040529150505b8051602080830191909120604080515f80825293810180835292909252601b908201526405ca1ab1e06060820152635ca1ab1e608082015260019060a0016020604051602081039080840390855afa158015612606573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811661267e576040517fcd16196600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040515f906126c39084906405ca1ab1e090635ca1ab1e90601b907fff0000000000000000000000000000000000000000000000000000000000000090602001614610565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529450505050505b949350505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314612756576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fa5b56b7906fd0a20e3f35120dd8343db1e12e037a6c90111c7e42885e82a1ce690602001610c44565b6040518060a00160405280606281526020016146fc6062913981565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff16331461283b576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60036128478282613fe9565b507f6b8f723a4c7a5335cafae8a598a0aa0301be1387c037dccc085b62add6448b2081604051610c44919061395c565b60085473ffffffffffffffffffffffffffffffffffffffff1680158015906128b5575073ffffffffffffffffffffffffffffffffffffffff81163314155b156128ec576040517f24eff8c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa158015612955573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129799190613f51565b156129b0576040517f39258d1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663604691696040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a1a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a3e9190613e5c565b905082811115612a7a576040517f2354600f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611388841115612ab6576040517fa29a6c7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612af873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016333084613617565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b62573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b869190613e5c565b6007805491925067ffffffffffffffff909116905f612ba48361435b565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550508585604051612bdb92919061466b565b6040519081900390208142612bf1600143613f70565b60408051602081019590955284019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166060830152406068820152608801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060075467ffffffffffffffff165f9081526006909352912055323303612ce7576007546040805183815233602082015260608183018190525f90820152905167ffffffffffffffff909216917ff94bb37db835f1ab585ee00041849a09b12cd081d77fa15ca070757619cbc9319181900360800190a26111d7565b60075460405167ffffffffffffffff909116907ff94bb37db835f1ab585ee00041849a09b12cd081d77fa15ca070757619cbc93190612d2d90849033908b908b9061467a565b60405180910390a2505050505050565b60025473ffffffffffffffffffffffffffffffffffffffff163314612d8e576040517f11e7be1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f819003612dc9576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e8811115612e05576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166379e2cf976040518163ffffffff1660e01b81526004015f604051808303815f87803b158015612e6a575f80fd5b505af1158015612e7c573d5f803e3d5ffd5b505050505f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16635ca1e1656040518163ffffffff1660e01b8152600401602060405180830381865afa158015612eea573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f0e9190613e5c565b60075460055491925042916801000000000000000090910467ffffffffffffffff1690815f5b8681101561322f575f8a8a83818110612f4f57612f4f61426c565b9050602002810190612f619190614299565b612f6a906142d5565b8051805160209091012060408201519192509067ffffffffffffffff161561314a5785612f968161435b565b9650505f81836020015184604001518560600151604051602001612ff89493929190938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff8a165f90815260069093529120549091508114613080576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208381015160408086015160608088015183519586018c90529285018790528481019390935260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166080840152908d901b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166088830152609c82015260bc0160405160208183030381529060405280519060200120955060065f8867ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f90555061321a565b8151516201d4c01015613189576040517fa29a6c7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516020810187905290810182905260608082018a905260c089901b7fffffffffffffffff0000000000000000000000000000000000000000000000001660808301528b901b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660888201525f609c82015260bc016040516020818303038152906040528051906020012094505b5050808061322790614381565b915050612f34565b5060075467ffffffffffffffff908116908416111561327a576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058290558567ffffffffffffffff8481169083161461332f575f61329f8386613ea0565b90506132b567ffffffffffffffff821683613f70565b91506132ee7f00000000000000000000000000000000000000000000000000000000000000008267ffffffffffffffff166121896107df565b50600780547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000067ffffffffffffffff8716021790555b61342b337f0000000000000000000000000000000000000000000000000000000000000000837f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663477fa2706040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133e29190613e5c565b6133ec91906143b8565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016929190613617565b6040517f9a908e7300000000000000000000000000000000000000000000000000000000815267ffffffffffffffff88166004820152602481018490525f907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af11580156134c7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134eb9190613f89565b90508067ffffffffffffffff167f3e54d0825ed78523037d00a81759237eb436ce774bd546993ee67a1b67b6e7668860405161352991815260200190565b60405180910390a25050505050505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526136129084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261367b565b505050565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526136759085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613590565b50505050565b5f6136dc826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166137869092919063ffffffff16565b80519091501561361257808060200190518101906136fa9190613f51565b613612576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610d8a565b60606126f884845f85855f808673ffffffffffffffffffffffffffffffffffffffff1685876040516137b891906146ea565b5f6040518083038185875af1925050503d805f81146137f2576040519150601f19603f3d011682016040523d82523d5f602084013e6137f7565b606091505b509150915061380887838387613813565b979650505050505050565b606083156138a85782515f036138a15773ffffffffffffffffffffffffffffffffffffffff85163b6138a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d8a565b50816126f8565b6126f883838151156138bd5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d8a919061395c565b5f5b8381101561390b5781810151838201526020016138f3565b50505f910152565b5f815180845261392a8160208601602086016138f1565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081525f61396e6020830184613913565b9392505050565b67ffffffffffffffff8116811461398a575f80fd5b50565b73ffffffffffffffffffffffffffffffffffffffff8116811461398a575f80fd5b5f805f606084860312156139c0575f80fd5b83356139cb81613975565b92506020840135915060408401356139e28161398d565b809150509250925092565b5f602082840312156139fd575f80fd5b813561396e81613975565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a7c57613a7c613a08565b604052919050565b5f67ffffffffffffffff821115613a9d57613a9d613a08565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b5f82601f830112613ad8575f80fd5b8135613aeb613ae682613a84565b613a35565b818152846020838601011115613aff575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f60a08688031215613b2f575f80fd5b8535613b3a8161398d565b94506020860135613b4a8161398d565b9350604086013567ffffffffffffffff80821115613b66575f80fd5b613b7289838a01613ac9565b94506060880135915080821115613b87575f80fd5b50613b9488828901613ac9565b95989497509295608001359392505050565b5f60208284031215613bb6575f80fd5b813561396e8161398d565b63ffffffff8116811461398a575f80fd5b5f805f805f8060c08789031215613be7575f80fd5b8635613bf28161398d565b95506020870135613c028161398d565b94506040870135613c1281613bc1565b93506060870135613c228161398d565b9250608087013567ffffffffffffffff80821115613c3e575f80fd5b613c4a8a838b01613ac9565b935060a0890135915080821115613c5f575f80fd5b50613c6c89828a01613ac9565b9150509295509295509295565b5f8083601f840112613c89575f80fd5b50813567ffffffffffffffff811115613ca0575f80fd5b6020830191508360208260051b8501011115613cba575f80fd5b9250929050565b5f8060208385031215613cd2575f80fd5b823567ffffffffffffffff811115613ce8575f80fd5b613cf485828601613c79565b90969095509350505050565b5f805f8060808587031215613d13575f80fd5b8435613d1e81613bc1565b93506020850135613d2e8161398d565b92506040850135613d3e81613bc1565b9150606085013567ffffffffffffffff811115613d59575f80fd5b613d6587828801613ac9565b91505092959194509250565b5f60208284031215613d81575f80fd5b813567ffffffffffffffff811115613d97575f80fd5b6126f884828501613ac9565b5f805f60408486031215613db5575f80fd5b833567ffffffffffffffff80821115613dcc575f80fd5b818601915086601f830112613ddf575f80fd5b813581811115613ded575f80fd5b876020828501011115613dfe575f80fd5b6020928301989097509590910135949350505050565b5f805f60408486031215613e26575f80fd5b833567ffffffffffffffff811115613e3c575f80fd5b613e4886828701613c79565b90945092505060208401356139e28161398d565b5f60208284031215613e6c575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b67ffffffffffffffff828116828216039080821115613ec157613ec1613e73565b5092915050565b5f82613efb577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b600181811c90821680613f1457607f821691505b602082108103613f4b577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b5f60208284031215613f61575f80fd5b8151801515811461396e575f80fd5b81810381811115613f8357613f83613e73565b92915050565b5f60208284031215613f99575f80fd5b815161396e81613975565b601f821115613612575f81815260208120601f850160051c81016020861015613fca5750805b601f850160051c820191505b818110156111d757828155600101613fd6565b815167ffffffffffffffff81111561400357614003613a08565b614017816140118454613f00565b84613fa4565b602080601f831160018114614069575f84156140335750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556111d7565b5f858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156140b557888601518255948401946001909101908401614096565b50858210156140f157878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b67ffffffffffffffff85168152608060208201525f6141236080830186613913565b905083604083015273ffffffffffffffffffffffffffffffffffffffff8316606083015295945050505050565b5f60208284031215614160575f80fd5b815167ffffffffffffffff811115614176575f80fd5b8201601f81018413614186575f80fd5b8051614194613ae682613a84565b8181528560208385010111156141a8575f80fd5b6141b98260208301602086016138f1565b95945050505050565b5f80604083850312156141d3575f80fd5b82516141de81613bc1565b60208401519092506141ef8161398d565b809150509250929050565b606081525f61420c6060830186613913565b905083602083015273ffffffffffffffffffffffffffffffffffffffff83166040830152949350505050565b67ffffffffffffffff818116838216019080821115613ec157613ec1613e73565b80820180821115613f8357613f83613e73565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818336030181126142cb575f80fd5b9190910192915050565b5f608082360312156142e5575f80fd5b6040516080810167ffffffffffffffff828210818311171561430957614309613a08565b81604052843591508082111561431d575f80fd5b5061432a36828601613ac9565b82525060208301356020820152604083013561434581613975565b6040820152606092830135928101929092525090565b5f67ffffffffffffffff80831681810361437757614377613e73565b6001019392505050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036143b1576143b1613e73565b5060010190565b8082028115828204841417613f8357613f83613e73565b5f63ffffffff808916835273ffffffffffffffffffffffffffffffffffffffff8089166020850152818816604085015280871660608501528086166080850152505060c060a083015261442560c0830184613913565b98975050505050505050565b61ffff818116838216019080821115613ec157613ec1613e73565b5f7fff00000000000000000000000000000000000000000000000000000000000000808a60f81b1683527fffff0000000000000000000000000000000000000000000000000000000000008960f01b16600184015287516144b4816003860160208c016138f1565b80840190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008860601b16600382015286516144f7816017840160208b016138f1565b808201915050818660f81b1660178201528451915061451d8260188301602088016138f1565b016018019998505050505050505050565b7fff000000000000000000000000000000000000000000000000000000000000008860f81b1681525f7fffff000000000000000000000000000000000000000000000000000000000000808960f01b1660018401528751614596816003860160208c016138f1565b80840190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008860601b16600382015286516145d9816017840160208b016138f1565b808201915050818660f01b166017820152845191506145ff8260198301602088016138f1565b016019019998505050505050505050565b5f8651614621818460208b016138f1565b9190910194855250602084019290925260f81b7fff000000000000000000000000000000000000000000000000000000000000009081166040840152166041820152604201919050565b818382375f9101908152919050565b84815273ffffffffffffffffffffffffffffffffffffffff8416602082015260606040820152816060820152818360808301375f818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01601019392505050565b5f82516142cb8184602087016138f156fedf2a8080944d5cf5032b2a844602278b01199ed191a86c93ff8080821092808000000000000000000000000000000000000000000000000000000005ca1ab1e000000000000000000000000000000000000000000000000000000005ca1ab1e01bffa26469706673582212201122ebb9845269aff998317e3b4fa53e1e51349d54cd85b323d97af344189ed464736f6c63430008140033000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f60000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab2
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106102e2575f3560e01c80637a5460c511610187578063c7fffd4b116100dd578063e46761c411610093578063ecef3f991161006e578063ecef3f991461079f578063f35dda47146107b2578063f851a440146107ba575f80fd5b8063e46761c414610751578063e7a7ed0214610778578063eaeb077b1461078c575f80fd5b8063cfa8ed47116100c3578063cfa8ed47146106ff578063d02103ca1461071f578063d7bc90ff14610746575f80fd5b8063c7fffd4b146106e4578063c89e42df146106ec575f80fd5b8063a3c573eb1161013d578063af7f3e0211610118578063af7f3e02146106a8578063b0afe154146106b0578063c754c7ed146106bc575f80fd5b8063a3c573eb1461065b578063a652f26c14610682578063ada8f91914610695575f80fd5b806391cafe321161016d57806391cafe321461061a5780639e0018771461062d5780639f26f84014610648575f80fd5b80637a5460c5146105d65780638c3d730114610612575f80fd5b8063456052671161023c5780635d6717a5116101f25780636e05d2cd116101cd5780636e05d2cd146105a75780636ff512cc146105b057806371257022146105c3575f80fd5b80635d6717a51461056d578063676870d2146105805780636b8616ce14610588575f80fd5b80634e487706116102225780634e4877061461051657806352bdeb6d14610529578063542028d514610565575f80fd5b806345605267146104b657806349b7b802146104ef575f80fd5b8063267822471161029c5780633c351e10116102775780633c351e10146104015780633cbc795b1461042157806340b5de6c1461045e575f80fd5b806326782247146103875780632c111c06146103cc57806332c2d153146103ec575f80fd5b806305835f37116102cc57806305835f371461031c578063107bf28c1461036557806311e892d41461036d575f80fd5b8062d0295d146102e65780630350896314610301575b5f80fd5b6102ee6107df565b6040519081526020015b60405180910390f35b610309602081565b60405161ffff90911681526020016102f8565b6103586040518060400160405280600881526020017f80808401c9c3809400000000000000000000000000000000000000000000000081525081565b6040516102f8919061395c565b6103586108e5565b61037560f981565b60405160ff90911681526020016102f8565b6001546103a79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102f8565b6008546103a79073ffffffffffffffffffffffffffffffffffffffff1681565b6103ff6103fa3660046139ae565b610971565b005b6009546103a79073ffffffffffffffffffffffffffffffffffffffff1681565b6009546104499074010000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016102f8565b6104857fff0000000000000000000000000000000000000000000000000000000000000081565b6040517fff0000000000000000000000000000000000000000000000000000000000000090911681526020016102f8565b6007546104d69068010000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016102f8565b6103a77f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab281565b6103ff6105243660046139ed565b610a40565b6103586040518060400160405280600281526020017f80b800000000000000000000000000000000000000000000000000000000000081525081565b610358610c4f565b6103ff61057b366004613b1b565b610c5c565b610309601f81565b6102ee6105963660046139ed565b60066020525f908152604090205481565b6102ee60055481565b6103ff6105be366004613ba6565b6111df565b6103ff6105d1366004613bd2565b6112a8565b6103586040518060400160405280600281526020017f80b900000000000000000000000000000000000000000000000000000000000081525081565b6103ff611aac565b6103ff610628366004613ba6565b611b7e565b6103a773a40d5f56745a118d0906a34e69aec8c0db1cb8fa81565b6103ff610656366004613cc1565b611c96565b6103a77f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede81565b610358610690366004613d00565b612322565b6103ff6106a3366004613ba6565b612700565b6103586127c9565b6102ee6405ca1ab1e081565b6007546104d690700100000000000000000000000000000000900467ffffffffffffffff1681565b61037560e481565b6103ff6106fa366004613d71565b6127e5565b6002546103a79073ffffffffffffffffffffffffffffffffffffffff1681565b6103a77f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb81565b6102ee635ca1ab1e81565b6103a77f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f681565b6007546104d69067ffffffffffffffff1681565b6103ff61079a366004613da3565b612877565b6103ff6107ad366004613e14565b612d3d565b610375601b81565b5f546103a79062010000900473ffffffffffffffffffffffffffffffffffffffff1681565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f90819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f616906370a0823190602401602060405180830381865afa15801561086b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061088f9190613e5c565b6007549091505f906108b99067ffffffffffffffff68010000000000000000820481169116613ea0565b67ffffffffffffffff169050805f036108d4575f9250505090565b6108de8183613ec8565b9250505090565b600480546108f290613f00565b80601f016020809104026020016040519081016040528092919081815260200182805461091e90613f00565b80156109695780601f1061094057610100808354040283529160200191610969565b820191905f5260205f20905b81548152906001019060200180831161094c57829003601f168201915b505050505081565b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1633146109e0576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff168367ffffffffffffffff167f9c72852172521097ba7e1482e6b44b351323df0155f97f4ea18fcec28e1f596684604051610a3391815260200190565b60405180910390a3505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314610a96576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62093a8067ffffffffffffffff82161115610add576040517ff5e37f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b46573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b6a9190613f51565b610bcb5760075467ffffffffffffffff700100000000000000000000000000000000909104811690821610610bcb576040517ff5e37f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000067ffffffffffffffff8416908102919091179091556040519081527fa7eb6cb8a613eb4e8bddc1ac3d61ec6cf10898760f0b187bcca794c6ca6fa40b906020015b60405180910390a150565b600380546108f290613f00565b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff163314610ccb576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54610100900460ff1615808015610ce957505f54600160ff909116105b80610d025750303b158015610d0257505f5460ff166001145b610d93576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610def575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b5f6040518060a00160405280606281526020016146fc6062913990505f818051906020012090505f4290505f7f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb73ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ea89190613e5c565b90505f868483858d610ebb600143613f70565b60408051602081019790975286019490945260608086019390935260c09190911b7fffffffffffffffff000000000000000000000000000000000000000000000000166080850152901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016608883015240609c82015260bc01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815290829052805160209091012060058190557f9a908e73000000000000000000000000000000000000000000000000000000008252600160048301526024820181905291505f907f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af1158015611003573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110279190613f89565b90508b5f60026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508a60025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555089600390816110b89190613fe9565b5060046110c58a82613fe9565b508b60085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062069780600760106101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507fd2c80353fc15ef62c6affc7cd6b7ab5b42c43290c50be3372e55ae552cecd19c8187858e6040516111679493929190614101565b60405180910390a150505050505080156111d7575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314611235576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527ff54144f9611984021529f814a1cb6a41e22c58351510a0d9f7e822618abb9cc090602001610c44565b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff163314611317576040517fb9b3a2c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54610100900460ff161580801561133557505f54600160ff909116105b8061134e5750303b15801561134e57505f5460ff166001145b6113da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610d8a565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611436575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b606073ffffffffffffffffffffffffffffffffffffffff851615611693576040517fc00f14ab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301527f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede169063c00f14ab906024015f60405180830381865afa1580156114dd573d5f803e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526115229190810190614150565b6040517f318aee3d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87811660048301529192505f9182917f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede9091169063318aee3d906024016040805180830381865afa1580156115b4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115d891906141c2565b915091508163ffffffff165f1461164f576009805463ffffffff841674010000000000000000000000000000000000000000027fffffffffffffffff00000000000000000000000000000000000000000000000090911673ffffffffffffffffffffffffffffffffffffffff841617179055611690565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff89161790555b50505b6009545f906116da90889073ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900463ffffffff1685612322565b90505f818051906020012090505f4290505f7f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb73ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611755573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117799190613e5c565b90505f808483858f61178c600143613f70565b60408051602081019790975286019490945260608086019390935260c09190911b7fffffffffffffffff000000000000000000000000000000000000000000000000166080850152901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016608883015240609c82015260bc01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815290829052805160209091012060058190557f9a908e73000000000000000000000000000000000000000000000000000000008252600160048301526024820181905291507f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af11580156118d2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118f69190613f89565b508c5f60026101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508b60025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555088600390816119869190613fe9565b5060046119938982613fe9565b508c60085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062069780600760106101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507f060116213bcbf54ca19fd649dc84b59ab2bbd200ab199770e4d923e222a28e7f85838e604051611a33939291906141fa565b60405180910390a15050505050508015611aa3575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611afd576040517fd1ec4b2300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001545f80547fffffffffffffffffffff0000000000000000000000000000000000000000ffff1673ffffffffffffffffffffffffffffffffffffffff9092166201000081029290921790556040519081527f056dc487bbf0795d0bbb1b4f0af523a855503cff740bfb4d5475f7a90c091e8e9060200160405180910390a1565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314611bd4576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60085473ffffffffffffffffffffffffffffffffffffffff16611c23576040517fc89374d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f5fbd7dd171301c4a1611a84aac4ba86d119478560557755f7927595b082634fb90602001610c44565b60085473ffffffffffffffffffffffffffffffffffffffff168015801590611cd4575073ffffffffffffffffffffffffffffffffffffffff81163314155b15611d0b576040517f24eff8c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4262093a807f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff166330c27dde6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d79573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d9d9190613f89565b611da79190614238565b67ffffffffffffffff161115611de9576040517f3d49ed4c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f819003611e24576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e8811115611e60576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60075467ffffffffffffffff80821691611e8891849168010000000000000000900416614259565b1115611ec0576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007546005546801000000000000000090910467ffffffffffffffff16905f5b8381101561215b575f878783818110611efb57611efb61426c565b9050602002810190611f0d9190614299565b611f16906142d5565b905083611f228161435b565b825180516020918201208185015160408087015160608801519151959a509295505f94611f8e948794929101938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff89165f90815260069093529120549091508114612016576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff86165f9081526006602052604081205561203a600188613f70565b84036120a95742600760109054906101000a900467ffffffffffffffff1684604001516120679190614238565b67ffffffffffffffff1611156120a9576040517fc44a082100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208381015160408086015160608088015183519586018b90529285018790528481019390935260c01b7fffffffffffffffff0000000000000000000000000000000000000000000000001660808401523390911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166088830152609c82015260bc01604051602081830303815290604052805190602001209450505050808061215390614381565b915050611ee0565b506121d17f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab2846121896107df565b61219391906143b8565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f616919061353e565b60058190556007805467ffffffffffffffff841668010000000000000000027fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff9091161790556040517f9a908e730000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab21690639a908e73906122a2908790869060040167ffffffffffffffff929092168252602082015260400190565b6020604051808303815f875af11580156122be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e29190613f89565b60405190915067ffffffffffffffff8216907f648a61dd2438f072f5a1960939abd30f37aea80d2e94c9792ad142d3e0a490a4905f90a250505050505050565b60605f85858573a40d5f56745a118d0906a34e69aec8c0db1cb8fa5f87604051602401612354969594939291906143cf565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167ff811bff70000000000000000000000000000000000000000000000000000000017905283519091506060905f036124a45760f9601f83516123e89190614431565b6040518060400160405280600881526020017f80808401c9c380940000000000000000000000000000000000000000000000008152507f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede6040518060400160405280600281526020017f80b800000000000000000000000000000000000000000000000000000000000081525060e48760405160200161248e979695949392919061444c565b60405160208183030381529060405290506125a8565b815161ffff10156124e1576040517f248b8f8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815160f96124f0602083614431565b6040518060400160405280600881526020017f80808401c9c380940000000000000000000000000000000000000000000000008152507f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede6040518060400160405280600281526020017f80b90000000000000000000000000000000000000000000000000000000000008152508588604051602001612595979695949392919061452e565b6040516020818303038152906040529150505b8051602080830191909120604080515f80825293810180835292909252601b908201526405ca1ab1e06060820152635ca1ab1e608082015260019060a0016020604051602081039080840390855afa158015612606573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811661267e576040517fcd16196600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040515f906126c39084906405ca1ab1e090635ca1ab1e90601b907fff0000000000000000000000000000000000000000000000000000000000000090602001614610565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529450505050505b949350505050565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff163314612756576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fa5b56b7906fd0a20e3f35120dd8343db1e12e037a6c90111c7e42885e82a1ce690602001610c44565b6040518060a00160405280606281526020016146fc6062913981565b5f5462010000900473ffffffffffffffffffffffffffffffffffffffff16331461283b576040517f4755657900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60036128478282613fe9565b507f6b8f723a4c7a5335cafae8a598a0aa0301be1387c037dccc085b62add6448b2081604051610c44919061395c565b60085473ffffffffffffffffffffffffffffffffffffffff1680158015906128b5575073ffffffffffffffffffffffffffffffffffffffff81163314155b156128ec576040517f24eff8c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa158015612955573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129799190613f51565b156129b0576040517f39258d1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1663604691696040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a1a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a3e9190613e5c565b905082811115612a7a576040517f2354600f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611388841115612ab6576040517fa29a6c7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612af873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f616333084613617565b5f7f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb73ffffffffffffffffffffffffffffffffffffffff16633ed691ef6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b62573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b869190613e5c565b6007805491925067ffffffffffffffff909116905f612ba48361435b565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550508585604051612bdb92919061466b565b6040519081900390208142612bf1600143613f70565b60408051602081019590955284019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166060830152406068820152608801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060075467ffffffffffffffff165f9081526006909352912055323303612ce7576007546040805183815233602082015260608183018190525f90820152905167ffffffffffffffff909216917ff94bb37db835f1ab585ee00041849a09b12cd081d77fa15ca070757619cbc9319181900360800190a26111d7565b60075460405167ffffffffffffffff909116907ff94bb37db835f1ab585ee00041849a09b12cd081d77fa15ca070757619cbc93190612d2d90849033908b908b9061467a565b60405180910390a2505050505050565b60025473ffffffffffffffffffffffffffffffffffffffff163314612d8e576040517f11e7be1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f819003612dc9576040517fcb591a5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e8811115612e05576040517fb59f753a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede73ffffffffffffffffffffffffffffffffffffffff166379e2cf976040518163ffffffff1660e01b81526004015f604051808303815f87803b158015612e6a575f80fd5b505af1158015612e7c573d5f803e3d5ffd5b505050505f7f000000000000000000000000580bda1e7a0cfae92fa7f6c20a3794f169ce3cfb73ffffffffffffffffffffffffffffffffffffffff16635ca1e1656040518163ffffffff1660e01b8152600401602060405180830381865afa158015612eea573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f0e9190613e5c565b60075460055491925042916801000000000000000090910467ffffffffffffffff1690815f5b8681101561322f575f8a8a83818110612f4f57612f4f61426c565b9050602002810190612f619190614299565b612f6a906142d5565b8051805160209091012060408201519192509067ffffffffffffffff161561314a5785612f968161435b565b9650505f81836020015184604001518560600151604051602001612ff89493929190938452602084019290925260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166040830152604882015260680190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012067ffffffffffffffff8a165f90815260069093529120549091508114613080576040517fce3d755e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208381015160408086015160608088015183519586018c90529285018790528481019390935260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166080840152908d901b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166088830152609c82015260bc0160405160208183030381529060405280519060200120955060065f8867ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f90555061321a565b8151516201d4c01015613189576040517fa29a6c7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516020810187905290810182905260608082018a905260c089901b7fffffffffffffffff0000000000000000000000000000000000000000000000001660808301528b901b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660888201525f609c82015260bc016040516020818303038152906040528051906020012094505b5050808061322790614381565b915050612f34565b5060075467ffffffffffffffff908116908416111561327a576040517fc630a00d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058290558567ffffffffffffffff8481169083161461332f575f61329f8386613ea0565b90506132b567ffffffffffffffff821683613f70565b91506132ee7f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab28267ffffffffffffffff166121896107df565b50600780547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000067ffffffffffffffff8716021790555b61342b337f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab2837f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1663477fa2706040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133e29190613e5c565b6133ec91906143b8565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000455e53cbb86018ac2b8092fdcd39d8444affc3f616929190613617565b6040517f9a908e7300000000000000000000000000000000000000000000000000000000815267ffffffffffffffff88166004820152602481018490525f907f0000000000000000000000005132a183e9f3cb7c848b0aac5ae0c4f0491b7ab273ffffffffffffffffffffffffffffffffffffffff1690639a908e73906044016020604051808303815f875af11580156134c7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134eb9190613f89565b90508067ffffffffffffffff167f3e54d0825ed78523037d00a81759237eb436ce774bd546993ee67a1b67b6e7668860405161352991815260200190565b60405180910390a25050505050505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526136129084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261367b565b505050565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526136759085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613590565b50505050565b5f6136dc826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166137869092919063ffffffff16565b80519091501561361257808060200190518101906136fa9190613f51565b613612576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610d8a565b60606126f884845f85855f808673ffffffffffffffffffffffffffffffffffffffff1685876040516137b891906146ea565b5f6040518083038185875af1925050503d805f81146137f2576040519150601f19603f3d011682016040523d82523d5f602084013e6137f7565b606091505b509150915061380887838387613813565b979650505050505050565b606083156138a85782515f036138a15773ffffffffffffffffffffffffffffffffffffffff85163b6138a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d8a565b50816126f8565b6126f883838151156138bd5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d8a919061395c565b5f5b8381101561390b5781810151838201526020016138f3565b50505f910152565b5f815180845261392a8160208601602086016138f1565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081525f61396e6020830184613913565b9392505050565b67ffffffffffffffff8116811461398a575f80fd5b50565b73ffffffffffffffffffffffffffffffffffffffff8116811461398a575f80fd5b5f805f606084860312156139c0575f80fd5b83356139cb81613975565b92506020840135915060408401356139e28161398d565b809150509250925092565b5f602082840312156139fd575f80fd5b813561396e81613975565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a7c57613a7c613a08565b604052919050565b5f67ffffffffffffffff821115613a9d57613a9d613a08565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b5f82601f830112613ad8575f80fd5b8135613aeb613ae682613a84565b613a35565b818152846020838601011115613aff575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f60a08688031215613b2f575f80fd5b8535613b3a8161398d565b94506020860135613b4a8161398d565b9350604086013567ffffffffffffffff80821115613b66575f80fd5b613b7289838a01613ac9565b94506060880135915080821115613b87575f80fd5b50613b9488828901613ac9565b95989497509295608001359392505050565b5f60208284031215613bb6575f80fd5b813561396e8161398d565b63ffffffff8116811461398a575f80fd5b5f805f805f8060c08789031215613be7575f80fd5b8635613bf28161398d565b95506020870135613c028161398d565b94506040870135613c1281613bc1565b93506060870135613c228161398d565b9250608087013567ffffffffffffffff80821115613c3e575f80fd5b613c4a8a838b01613ac9565b935060a0890135915080821115613c5f575f80fd5b50613c6c89828a01613ac9565b9150509295509295509295565b5f8083601f840112613c89575f80fd5b50813567ffffffffffffffff811115613ca0575f80fd5b6020830191508360208260051b8501011115613cba575f80fd5b9250929050565b5f8060208385031215613cd2575f80fd5b823567ffffffffffffffff811115613ce8575f80fd5b613cf485828601613c79565b90969095509350505050565b5f805f8060808587031215613d13575f80fd5b8435613d1e81613bc1565b93506020850135613d2e8161398d565b92506040850135613d3e81613bc1565b9150606085013567ffffffffffffffff811115613d59575f80fd5b613d6587828801613ac9565b91505092959194509250565b5f60208284031215613d81575f80fd5b813567ffffffffffffffff811115613d97575f80fd5b6126f884828501613ac9565b5f805f60408486031215613db5575f80fd5b833567ffffffffffffffff80821115613dcc575f80fd5b818601915086601f830112613ddf575f80fd5b813581811115613ded575f80fd5b876020828501011115613dfe575f80fd5b6020928301989097509590910135949350505050565b5f805f60408486031215613e26575f80fd5b833567ffffffffffffffff811115613e3c575f80fd5b613e4886828701613c79565b90945092505060208401356139e28161398d565b5f60208284031215613e6c575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b67ffffffffffffffff828116828216039080821115613ec157613ec1613e73565b5092915050565b5f82613efb577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b600181811c90821680613f1457607f821691505b602082108103613f4b577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b5f60208284031215613f61575f80fd5b8151801515811461396e575f80fd5b81810381811115613f8357613f83613e73565b92915050565b5f60208284031215613f99575f80fd5b815161396e81613975565b601f821115613612575f81815260208120601f850160051c81016020861015613fca5750805b601f850160051c820191505b818110156111d757828155600101613fd6565b815167ffffffffffffffff81111561400357614003613a08565b614017816140118454613f00565b84613fa4565b602080601f831160018114614069575f84156140335750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556111d7565b5f858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156140b557888601518255948401946001909101908401614096565b50858210156140f157878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b67ffffffffffffffff85168152608060208201525f6141236080830186613913565b905083604083015273ffffffffffffffffffffffffffffffffffffffff8316606083015295945050505050565b5f60208284031215614160575f80fd5b815167ffffffffffffffff811115614176575f80fd5b8201601f81018413614186575f80fd5b8051614194613ae682613a84565b8181528560208385010111156141a8575f80fd5b6141b98260208301602086016138f1565b95945050505050565b5f80604083850312156141d3575f80fd5b82516141de81613bc1565b60208401519092506141ef8161398d565b809150509250929050565b606081525f61420c6060830186613913565b905083602083015273ffffffffffffffffffffffffffffffffffffffff83166040830152949350505050565b67ffffffffffffffff818116838216019080821115613ec157613ec1613e73565b80820180821115613f8357613f83613e73565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818336030181126142cb575f80fd5b9190910192915050565b5f608082360312156142e5575f80fd5b6040516080810167ffffffffffffffff828210818311171561430957614309613a08565b81604052843591508082111561431d575f80fd5b5061432a36828601613ac9565b82525060208301356020820152604083013561434581613975565b6040820152606092830135928101929092525090565b5f67ffffffffffffffff80831681810361437757614377613e73565b6001019392505050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036143b1576143b1613e73565b5060010190565b8082028115828204841417613f8357613f83613e73565b5f63ffffffff808916835273ffffffffffffffffffffffffffffffffffffffff8089166020850152818816604085015280871660608501528086166080850152505060c060a083015261442560c0830184613913565b98975050505050505050565b61ffff818116838216019080821115613ec157613ec1613e73565b5f7fff00000000000000000000000000000000000000000000000000000000000000808a60f81b1683527fffff0000000000000000000000000000000000000000000000000000000000008960f01b16600184015287516144b4816003860160208c016138f1565b80840190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008860601b16600382015286516144f7816017840160208b016138f1565b808201915050818660f81b1660178201528451915061451d8260188301602088016138f1565b016018019998505050505050505050565b7fff000000000000000000000000000000000000000000000000000000000000008860f81b1681525f7fffff000000000000000000000000000000000000000000000000000000000000808960f01b1660018401528751614596816003860160208c016138f1565b80840190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008860601b16600382015286516145d9816017840160208b016138f1565b808201915050818660f01b166017820152845191506145ff8260198301602088016138f1565b016019019998505050505050505050565b5f8651614621818460208b016138f1565b9190910194855250602084019290925260f81b7fff000000000000000000000000000000000000000000000000000000000000009081166040840152166041820152604201919050565b818382375f9101908152919050565b84815273ffffffffffffffffffffffffffffffffffffffff8416602082015260606040820152816060820152818360808301375f818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01601019392505050565b5f82516142cb8184602087016138f156fedf2a8080944d5cf5032b2a844602278b01199ed191a86c93ff8080821092808000000000000000000000000000000000000000000000000000000005ca1ab1e000000000000000000000000000000000000000000000000000000005ca1ab1e01bffa26469706673582212201122ebb9845269aff998317e3b4fa53e1e51349d54cd85b323d97af344189ed464736f6c63430008140033
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 | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.