More Info
Private Name Tags
ContractCreator
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
48701812 | 96 days ago | Contract Creation | 0 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
Grants
Compiler Version
v0.8.23+commit.f704f362
ZkSolc Version
v1.4.1
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity ^0.8.23; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @title Grants * @notice Manages time-based token vesting schedules for ERC-20 tokens, allowing for the creation, * claiming, and cancellation of vesting schedules. * @dev Uses the SafeERC20 library to interact with ERC20 tokens securely. */ contract Grants { using SafeERC20 for IERC20; // Token used for vesting. IERC20 public immutable token; // Minimum amount of tokens that can be vested per period. This is a safety bound to prevent dusting attacks. uint256 public immutable perPeriodMinAmount; // Maximum number of vesting schedules per address per page. uint8 public immutable pageLimit; // Mapping of addresses to the current page of their vesting schedules. // The current page is the last page that has been used to store a vesting schedule. mapping(address => uint256) public currentPage; // Mapping of addresses to their vesting schedules split into pages. mapping(address => mapping(uint256 => VestingSchedule[])) public vestingSchedules; struct VestingSchedule { address cancelAuthority; // Address authorized to cancel the vesting. uint256 start; // Timestamp when vesting starts. uint256 period; // Duration of each period. uint32 periodCount; // Total number of periods. uint256 perPeriodAmount; // Amount of tokens distributed each period. } // Events event VestingScheduleAdded(address indexed to, VestingSchedule schedule); // start and end indicate the range of the grant pages that are iterated over for claiming. event Claimed(address indexed who, uint256 amount, uint256 start, uint256 end); // start and end indicate the range of the grant pages that are iterated over for cancelling. event VestingSchedulesCanceled(address indexed from, address indexed to, uint256 start, uint256 end); // start and end indicate the range of the grant pages that are iterated over for renouncing. event Renounced(address indexed from, address indexed to, uint256 start, uint256 end); // Errors error InvalidZeroParameter(); // Parameters such as some addresses and periods, periodCounts must be non-zero. error VestingToSelf(); // Thrown when the creator attempts to vest tokens to themselves. error NoOpIsFailure(); // Thrown when an operation that should change state does not. error LowVestingAmount(); // Thrown when the amount to be vested is below the minimum allowed. error InvalidPage(); // Thrown when the page is invalid. constructor(address _token, uint256 _perPeriodMinAmount, uint8 _pageLimit) { if (_pageLimit == 0) { revert InvalidZeroParameter(); } token = IERC20(_token); perPeriodMinAmount = _perPeriodMinAmount; pageLimit = _pageLimit; } /** * @notice Adds a vesting schedule to an account. * @param to Recipient of the tokens under the vesting schedule. * @param start Start time of the vesting schedule as a Unix timestamp. * @param period Duration of each period in seconds. * @param periodCount Number of periods in the vesting schedule. * @param perPeriodAmount Amount of tokens to be released each period. * @param cancelAuthority Address that has the authority to cancel the vesting. If set to address(0), no one can cancel. */ function addVestingSchedule( address to, uint256 start, uint256 period, uint32 periodCount, uint256 perPeriodAmount, address cancelAuthority ) external { validateVestingSchedule(to, period, periodCount, perPeriodAmount); VestingSchedule memory schedule = VestingSchedule(cancelAuthority, start, period, periodCount, perPeriodAmount); uint256 page = currentPage[to]; if (vestingSchedules[to][page].length == pageLimit) { page += 1; currentPage[to] = page; } vestingSchedules[to][page].push(schedule); emit VestingScheduleAdded(to, schedule); token.safeTransferFrom(msg.sender, address(this), perPeriodAmount * periodCount); } /** * @notice Checks if the vesting schedule parameters are valid. * @param to Recipient of the tokens under the vesting schedule. * @param period Duration of each period in seconds. * @param periodCount Number of periods in the vesting schedule. * @param perPeriodAmount Amount of tokens to be released each period. */ function validateVestingSchedule(address to, uint256 period, uint32 periodCount, uint256 perPeriodAmount) public view { _mustBeNonZeroAddress(to); _mustNotBeSelf(to); _mustBeNonZero(period); _mustBeNonZero(periodCount); _mustBeEqualOrExceedMinAmount(perPeriodAmount); } /** * @notice Claims all vested tokens available for msg.sender up to the current block timestamp. * @param start Start page of the vesting schedules to claim from. * @param end the page after the last page of the vesting schedules to claim from. * @dev if start == end == 0, all pages will be used for claim. */ function claim(uint256 start, uint256 end) external { uint256 totalClaimable = 0; uint256 currentTime = block.timestamp; (start, end) = _sanitizePageRange(msg.sender, start, end); for (uint256 page = start; page < end; page++) { uint256 i = 0; VestingSchedule[] storage schedules = vestingSchedules[msg.sender][page]; while (i < schedules.length) { VestingSchedule storage schedule = schedules[i]; if (currentTime > schedule.start) { uint256 periodsElapsed = (currentTime - schedule.start) / schedule.period; uint256 effectivePeriods = periodsElapsed > schedule.periodCount ? schedule.periodCount : periodsElapsed; uint256 claimable = effectivePeriods * schedule.perPeriodAmount; schedule.periodCount -= uint32(effectivePeriods); schedule.start += periodsElapsed * schedule.period; totalClaimable += claimable; if (schedule.periodCount == 0) { schedules[i] = schedules[schedules.length - 1]; schedules.pop(); continue; } } i++; } } if (totalClaimable > 0) { token.safeTransfer(msg.sender, totalClaimable); emit Claimed(msg.sender, totalClaimable, start, end); } else { revert NoOpIsFailure(); } } /** * @notice Renounces the cancel authority for all of the msg.sender's vesting schedules directed to a specific recipient. * @param to Recipient of the vesting whose schedules are affected. * @param start Start page of the vesting schedules to renounce from. * @param end the page after the last page of the vesting schedules to renounce from. * @dev if start == end == 0, all pages will be used for renounce. */ function renounce(address to, uint256 start, uint256 end) external { bool anySchedulesFound = false; (start, end) = _sanitizePageRange(to, start, end); for (uint256 page = start; page < end; page++) { VestingSchedule[] storage schedules = vestingSchedules[to][page]; for (uint256 i = 0; i < schedules.length; i++) { if (schedules[i].cancelAuthority == msg.sender) { schedules[i].cancelAuthority = address(0); anySchedulesFound = true; } } } if (!anySchedulesFound) { revert NoOpIsFailure(); } else { emit Renounced(msg.sender, to, start, end); } } /** * @notice Cancels all vesting schedules of a specific recipient, initiated by the cancel authority. * @param to Recipient whose schedules will be canceled. * @param start Start page of the vesting schedules to cancel. * @param end the page after the last page of the vesting schedules to cancel. * @dev if start == end == 0, all pages will be used for cancel. */ function cancelVestingSchedules(address to, uint256 start, uint256 end) external { uint256 totalClaimable = 0; uint256 totalRedeemable = 0; uint256 currentTime = block.timestamp; (start, end) = _sanitizePageRange(to, start, end); for (uint256 page = start; page < end; page++) { uint256 i = 0; VestingSchedule[] storage schedules = vestingSchedules[to][page]; while (i < schedules.length) { VestingSchedule storage schedule = schedules[i]; if (schedule.cancelAuthority == msg.sender) { uint256 periodsElapsed = currentTime > schedule.start ? (currentTime - schedule.start) / schedule.period : 0; uint256 effectivePeriods = periodsElapsed > schedule.periodCount ? schedule.periodCount : periodsElapsed; uint256 claimable = effectivePeriods * schedule.perPeriodAmount; uint256 redeemable = (schedule.periodCount - effectivePeriods) * schedule.perPeriodAmount; totalClaimable += claimable; totalRedeemable += redeemable; schedules[i] = schedules[schedules.length - 1]; schedules.pop(); continue; } i++; } } if (totalClaimable == 0 && totalRedeemable == 0) { revert NoOpIsFailure(); } if (totalClaimable > 0) { token.safeTransfer(to, totalClaimable); } if (totalRedeemable > 0) { token.safeTransfer(msg.sender, totalRedeemable); } emit VestingSchedulesCanceled(msg.sender, to, start, end); } /** * @notice Returns the number of vesting schedules associated with a given address. * @param to The address whose schedule count is to be queried. * @return The number of vesting schedules associated with the address. */ function getGrantsCount(address to) external view returns (uint256) { uint256 count = 0; for (uint256 i = 0; i <= currentPage[to]; i++) { count += vestingSchedules[to][i].length; } return count; } // Private helper functions function _mustBeNonZero(uint256 value) private pure { if (value == 0) { revert InvalidZeroParameter(); } } function _mustBeNonZeroAddress(address value) private pure { if (value == address(0)) { revert InvalidZeroParameter(); } } function _mustNotBeSelf(address to) private view { if (msg.sender == to) { revert VestingToSelf(); } } function _mustBeEqualOrExceedMinAmount(uint256 amount) private view { if (amount < perPeriodMinAmount) { revert LowVestingAmount(); } } function _sanitizePageRange(address grantee, uint256 start, uint256 end) private view returns (uint256, uint256) { uint256 endPage = currentPage[grantee] + 1; if (start > end || start >= endPage) { revert InvalidPage(); } if (end > endPage || end == 0) { end = endPage; } return (start, end); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "./IAccessControl.sol"; import {Context} from "../utils/Context.sol"; import {ERC165} from "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } mapping(bytes32 role => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with an {AccessControlUnauthorizedAccount} error including the required role. */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual returns (bool) { return _roles[role].hasRole[account]; } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()` * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account` * is missing `role`. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AccessControlUnauthorizedAccount(account, role); } } /** * @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 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 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 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 `callerConfirmation`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessControlBadConfirmation(); } _revokeRole(role, callerConfirmation); } /** * @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual returns (bool) { if (!hasRole(role, account)) { _roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual returns (bool) { if (hasRole(role, account)) { _roles[role].hasRole[account] = false; emit RoleRevoked(role, account, _msgSender()); return true; } else { return false; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol) pragma solidity ^0.8.20; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev The `account` is missing a role. */ error AccessControlUnauthorizedAccount(address account, bytes32 neededRole); /** * @dev The caller of a function is not the expected one. * * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}. */ error AccessControlBadConfirmation(); /** * @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. */ 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 `callerConfirmation`. */ function renounceRole(bytes32 role, address callerConfirmation) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol) pragma solidity ^0.8.20; /** * @dev Standard ERC20 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens. */ interface IERC20Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC20InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC20InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. * @param spender Address that may be allowed to operate on tokens without being their owner. * @param allowance Amount of tokens a `spender` is allowed to operate with. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC20InvalidApprover(address approver); /** * @dev Indicates a failure with the `spender` to be approved. Used in approvals. * @param spender Address that may be allowed to operate on tokens without being their owner. */ error ERC20InvalidSpender(address spender); } /** * @dev Standard ERC721 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20. * Used in balance queries. * @param owner Address of the current owner of a token. */ error ERC721InvalidOwner(address owner); /** * @dev Indicates a `tokenId` whose `owner` is the zero address. * @param tokenId Identifier number of a token. */ error ERC721NonexistentToken(uint256 tokenId); /** * @dev Indicates an error related to the ownership over a particular token. Used in transfers. * @param sender Address whose tokens are being transferred. * @param tokenId Identifier number of a token. * @param owner Address of the current owner of a token. */ error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC721InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC721InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param tokenId Identifier number of a token. */ error ERC721InsufficientApproval(address operator, uint256 tokenId); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC721InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC721InvalidOperator(address operator); } /** * @dev Standard ERC1155 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens. */ interface IERC1155Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. * @param tokenId Identifier number of a token. */ error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC1155InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC1155InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param owner Address of the current owner of a token. */ error ERC1155MissingApprovalForAll(address operator, address owner); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC1155InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC1155InvalidOperator(address operator); /** * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. * Used in batch transfers. * @param idsLength Length of the array of token identifiers * @param valuesLength Length of the array of token amounts */ error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC1271 standard signature validation method for * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. */ interface IERC1271 { /** * @dev Should return whether the signature provided is valid for the provided data * @param hash Hash of the data to be signed * @param signature Signature byte array associated with _data */ function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4906.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.sol"; import {IERC721} from "./IERC721.sol"; /// @title EIP-721 Metadata Update Extension interface IERC4906 is IERC165, IERC721 { /// @dev This event emits when the metadata of a token is changed. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFT. event MetadataUpdate(uint256 _tokenId); /// @dev This event emits when the metadata of a range of tokens is changed. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFTs. event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol) pragma solidity ^0.8.20; interface IERC5267 { /** * @dev MAY be emitted to signal that the domain could have changed. */ event EIP712DomainChanged(); /** * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712 * signature. */ function eip712Domain() external view returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC721.sol) pragma solidity ^0.8.20; import {IERC721} from "../token/ERC721/IERC721.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC20Metadata} from "./extensions/IERC20Metadata.sol"; import {Context} from "../../utils/Context.sol"; import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. */ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { mapping(address account => uint256) private _balances; mapping(address account => mapping(address spender => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `value`. */ function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `value`. * - the caller must have allowance for ``from``'s tokens of at least * `value`. */ function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; } /** * @dev Moves a `value` amount of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); } /** * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding * this function. * * Emits a {Transfer} event. */ function _update(address from, address to, uint256 value) internal virtual { if (from == address(0)) { // Overflow check required: The rest of the code assumes that totalSupply never overflows _totalSupply += value; } else { uint256 fromBalance = _balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { // Overflow not possible: value <= fromBalance <= totalSupply. _balances[from] = fromBalance - value; } } if (to == address(0)) { unchecked { // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. _totalSupply -= value; } } else { unchecked { // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. _balances[to] += value; } } emit Transfer(from, to, value); } /** * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). * Relies on the `_update` mechanism * * Emits a {Transfer} event with `from` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } /** * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. * Relies on the `_update` mechanism. * * Emits a {Transfer} event with `to` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead */ function _burn(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidSender(address(0)); } _update(account, address(0), value); } /** * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); } /** * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. * * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any * `Approval` event during `transferFrom` operations. * * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to * true using the following override: * ``` * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { * super._approve(owner, spender, value, true); * } * ``` * * Requirements are the same as {_approve}. */ function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } _allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } } /** * @dev Updates `owner` s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. * * Does not emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Burnable.sol) pragma solidity ^0.8.20; import {ERC20} from "../ERC20.sol"; import {Context} from "../../../utils/Context.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { /** * @dev Destroys a `value` amount of tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 value) public virtual { _burn(_msgSender(), value); } /** * @dev Destroys a `value` amount of tokens from `account`, deducting from * the caller's allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `value`. */ function burnFrom(address account, uint256 value) public virtual { _spendAllowance(account, _msgSender(), value); _burn(account, value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata is IERC20 { /** * @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 v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` 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 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol) pragma solidity ^0.8.20; import {IERC721} from "./IERC721.sol"; import {IERC721Receiver} from "./IERC721Receiver.sol"; import {IERC721Metadata} from "./extensions/IERC721Metadata.sol"; import {Context} from "../../utils/Context.sol"; import {Strings} from "../../utils/Strings.sol"; import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol"; import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol"; /** * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including * the Metadata extension, but not including the Enumerable extension, which is available separately as * {ERC721Enumerable}. */ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors { using Strings for uint256; // Token name string private _name; // Token symbol string private _symbol; mapping(uint256 tokenId => address) private _owners; mapping(address owner => uint256) private _balances; mapping(uint256 tokenId => address) private _tokenApprovals; mapping(address owner => mapping(address operator => bool)) private _operatorApprovals; /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view virtual returns (uint256) { if (owner == address(0)) { revert ERC721InvalidOwner(address(0)); } return _balances[owner]; } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view virtual returns (address) { return _requireOwned(tokenId); } /** * @dev See {IERC721Metadata-name}. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual returns (string memory) { _requireOwned(tokenId); string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : ""; } /** * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each * token will be the concatenation of the `baseURI` and the `tokenId`. Empty * by default, can be overridden in child contracts. */ function _baseURI() internal view virtual returns (string memory) { return ""; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual { _approve(to, tokenId, _msgSender()); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view virtual returns (address) { _requireOwned(tokenId); return _getApproved(tokenId); } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom(address from, address to, uint256 tokenId) public virtual { if (to == address(0)) { revert ERC721InvalidReceiver(address(0)); } // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here. address previousOwner = _update(to, tokenId, _msgSender()); if (previousOwner != from) { revert ERC721IncorrectOwner(from, tokenId, previousOwner); } } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId) public { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual { transferFrom(from, to, tokenId); _checkOnERC721Received(from, to, tokenId, data); } /** * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist * * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the * core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`. */ function _ownerOf(uint256 tokenId) internal view virtual returns (address) { return _owners[tokenId]; } /** * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted. */ function _getApproved(uint256 tokenId) internal view virtual returns (address) { return _tokenApprovals[tokenId]; } /** * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in * particular (ignoring whether it is owned by `owner`). * * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this * assumption. */ function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) { return spender != address(0) && (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender); } /** * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner. * Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets * the `spender` for the specific `tokenId`. * * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this * assumption. */ function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual { if (!_isAuthorized(owner, spender, tokenId)) { if (owner == address(0)) { revert ERC721NonexistentToken(tokenId); } else { revert ERC721InsufficientApproval(spender, tokenId); } } } /** * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override. * * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that * a uint256 would ever overflow from increments when these increments are bounded to uint128 values. * * WARNING: Increasing an account's balance using this function tends to be paired with an override of the * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership * remain consistent with one another. */ function _increaseBalance(address account, uint128 value) internal virtual { unchecked { _balances[account] += value; } } /** * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update. * * The `auth` argument is optional. If the value passed is non 0, then this function will check that * `auth` is either the owner of the token, or approved to operate on the token (by the owner). * * Emits a {Transfer} event. * * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}. */ function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) { address from = _ownerOf(tokenId); // Perform (optional) operator check if (auth != address(0)) { _checkAuthorized(from, auth, tokenId); } // Execute the update if (from != address(0)) { // Clear approval. No need to re-authorize or emit the Approval event _approve(address(0), tokenId, address(0), false); unchecked { _balances[from] -= 1; } } if (to != address(0)) { unchecked { _balances[to] += 1; } } _owners[tokenId] = to; emit Transfer(from, to, tokenId); return from; } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal { if (to == address(0)) { revert ERC721InvalidReceiver(address(0)); } address previousOwner = _update(to, tokenId, address(0)); if (previousOwner != address(0)) { revert ERC721InvalidSender(address(0)); } } /** * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance. * * Requirements: * * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual { _mint(to, tokenId); _checkOnERC721Received(address(0), to, tokenId, data); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * This is an internal function that does not check if the sender is authorized to operate on the token. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal { address previousOwner = _update(address(0), tokenId, address(0)); if (previousOwner == address(0)) { revert ERC721NonexistentToken(tokenId); } } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer(address from, address to, uint256 tokenId) internal { if (to == address(0)) { revert ERC721InvalidReceiver(address(0)); } address previousOwner = _update(to, tokenId, address(0)); if (previousOwner == address(0)) { revert ERC721NonexistentToken(tokenId); } else if (previousOwner != from) { revert ERC721IncorrectOwner(from, tokenId, previousOwner); } } /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients * are aware of the ERC721 standard to prevent tokens from being forever locked. * * `data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is like {safeTransferFrom} in the sense that it invokes * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `tokenId` token must exist and be owned by `from`. * - `to` cannot be the zero address. * - `from` cannot be the zero address. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer(address from, address to, uint256 tokenId) internal { _safeTransfer(from, to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual { _transfer(from, to, tokenId); _checkOnERC721Received(from, to, tokenId, data); } /** * @dev Approve `to` to operate on `tokenId` * * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is * either the owner of the token, or approved to operate on all tokens held by this owner. * * Emits an {Approval} event. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address to, uint256 tokenId, address auth) internal { _approve(to, tokenId, auth, true); } /** * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not * emitted in the context of transfers. */ function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual { // Avoid reading the owner unless necessary if (emitEvent || auth != address(0)) { address owner = _requireOwned(tokenId); // We do not use _isAuthorized because single-token approvals should not be able to call approve if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) { revert ERC721InvalidApprover(auth); } if (emitEvent) { emit Approval(owner, to, tokenId); } } _tokenApprovals[tokenId] = to; } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Requirements: * - operator can't be the address zero. * * Emits an {ApprovalForAll} event. */ function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { if (operator == address(0)) { revert ERC721InvalidOperator(operator); } _operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned). * Returns the owner. * * Overrides to ownership logic should be done to {_ownerOf}. */ function _requireOwned(uint256 tokenId) internal view returns (address) { address owner = _ownerOf(tokenId); if (owner == address(0)) { revert ERC721NonexistentToken(tokenId); } return owner; } /** * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the * recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param data bytes optional data to send along with the call */ function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private { if (to.code.length > 0) { try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { if (retval != IERC721Receiver.onERC721Received.selector) { revert ERC721InvalidReceiver(to); } } catch (bytes memory reason) { if (reason.length == 0) { revert ERC721InvalidReceiver(to); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721URIStorage.sol) pragma solidity ^0.8.20; import {ERC721} from "../ERC721.sol"; import {Strings} from "../../../utils/Strings.sol"; import {IERC4906} from "../../../interfaces/IERC4906.sol"; import {IERC165} from "../../../interfaces/IERC165.sol"; /** * @dev ERC721 token with storage based token URI management. */ abstract contract ERC721URIStorage is IERC4906, ERC721 { using Strings for uint256; // Interface ID as defined in ERC-4906. This does not correspond to a traditional interface ID as ERC-4906 only // defines events and does not include any external function. bytes4 private constant ERC4906_INTERFACE_ID = bytes4(0x49064906); // Optional mapping for token URIs mapping(uint256 tokenId => string) private _tokenURIs; /** * @dev See {IERC165-supportsInterface} */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, IERC165) returns (bool) { return interfaceId == ERC4906_INTERFACE_ID || super.supportsInterface(interfaceId); } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { _requireOwned(tokenId); string memory _tokenURI = _tokenURIs[tokenId]; string memory base = _baseURI(); // If there is no base URI, return the token URI. if (bytes(base).length == 0) { return _tokenURI; } // If both are set, concatenate the baseURI and tokenURI (via string.concat). if (bytes(_tokenURI).length > 0) { return string.concat(base, _tokenURI); } return super.tokenURI(tokenId); } /** * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. * * Emits {MetadataUpdate}. */ function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { _tokenURIs[tokenId] = _tokenURI; emit MetadataUpdate(tokenId); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.20; import {IERC721} from "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or * {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the address zero. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.20; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be * reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// 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.1) (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; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.20; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS } /** * @dev The signature derives the `address(0)`. */ error ECDSAInvalidSignature(); /** * @dev The signature has an invalid length. */ error ECDSAInvalidSignatureLength(uint256 length); /** * @dev The signature has an S value that is in the upper half order. */ error ECDSAInvalidSignatureS(bytes32 s); /** * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not * return address(0) without also returning an error description. Errors are documented using an enum (error type) * and a bytes32 providing additional information about the error. * * If no error is returned, then the address can be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { unchecked { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); // We do not check for an overflow here since the shift operation results in 0 or 1. uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError, bytes32) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS, s); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature, bytes32(0)); } return (signer, RecoverError.NoError, bytes32(0)); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s); _throwError(error, errorArg); return recovered; } /** * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. */ function _throwError(RecoverError error, bytes32 errorArg) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert ECDSAInvalidSignature(); } else if (error == RecoverError.InvalidSignatureLength) { revert ECDSAInvalidSignatureLength(uint256(errorArg)); } else if (error == RecoverError.InvalidSignatureS) { revert ECDSAInvalidSignatureS(errorArg); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.20; import {MessageHashUtils} from "./MessageHashUtils.sol"; import {ShortStrings, ShortString} from "../ShortStrings.sol"; import {IERC5267} from "../../interfaces/IERC5267.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. * * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the * separator from the immutable values, which is cheaper than accessing a cached version in cold storage. * * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ abstract contract EIP712 is IERC5267 { using ShortStrings for *; bytes32 private constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to // invalidate the cached domain separator if the chain id changes. bytes32 private immutable _cachedDomainSeparator; uint256 private immutable _cachedChainId; address private immutable _cachedThis; bytes32 private immutable _hashedName; bytes32 private immutable _hashedVersion; ShortString private immutable _name; ShortString private immutable _version; string private _nameFallback; string private _versionFallback; /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ constructor(string memory name, string memory version) { _name = name.toShortStringWithFallback(_nameFallback); _version = version.toShortStringWithFallback(_versionFallback); _hashedName = keccak256(bytes(name)); _hashedVersion = keccak256(bytes(version)); _cachedChainId = block.chainid; _cachedDomainSeparator = _buildDomainSeparator(); _cachedThis = address(this); } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { if (address(this) == _cachedThis && block.chainid == _cachedChainId) { return _cachedDomainSeparator; } else { return _buildDomainSeparator(); } } function _buildDomainSeparator() private view returns (bytes32) { return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash); } /** * @dev See {IERC-5267}. */ function eip712Domain() public view virtual returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { return ( hex"0f", // 01111 _EIP712Name(), _EIP712Version(), block.chainid, address(this), bytes32(0), new uint256[](0) ); } /** * @dev The name parameter for the EIP712 domain. * * NOTE: By default this function reads _name which is an immutable value. * It only reads from storage if necessary (in case the value is too large to fit in a ShortString). */ // solhint-disable-next-line func-name-mixedcase function _EIP712Name() internal view returns (string memory) { return _name.toStringWithFallback(_nameFallback); } /** * @dev The version parameter for the EIP712 domain. * * NOTE: By default this function reads _version which is an immutable value. * It only reads from storage if necessary (in case the value is too large to fit in a ShortString). */ // solhint-disable-next-line func-name-mixedcase function _EIP712Version() internal view returns (string memory) { return _version.toStringWithFallback(_versionFallback); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol) pragma solidity ^0.8.20; import {Strings} from "../Strings.sol"; /** * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. * * The library provides methods for generating a hash of a message that conforms to the * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] * specifications. */ library MessageHashUtils { /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing a bytes32 `messageHash` with * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with * keccak256, although any bytes32 value can be safely used because the final digest will * be re-hashed. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20) } } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing an arbitrary `message` with * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) { return keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message)); } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x00` (data with intended validator). * * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended * `validator` address. Then hashing the result. * * See {ECDSA-recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked(hex"19_00", validator, data)); } /** * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). * * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with * `\x19\x01` and hashing the result. It corresponds to the hash signed by the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. * * See {ECDSA-recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, hex"19_01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) digest := keccak256(ptr, 0x42) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol) pragma solidity ^0.8.20; import {ECDSA} from "./ECDSA.sol"; import {IERC1271} from "../../interfaces/IERC1271.sol"; /** * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like * Argent and Safe Wallet (previously Gnosis Safe). */ library SignatureChecker { /** * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) { (address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature); return (error == ECDSA.RecoverError.NoError && recovered == signer) || isValidERC1271SignatureNow(signer, hash, signature); } /** * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated * against the signer smart contract using ERC1271. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidERC1271SignatureNow( address signer, bytes32 hash, bytes memory signature ) internal view returns (bool) { (bool success, bytes memory result) = signer.staticcall( abi.encodeCall(IERC1271.isValidSignature, (hash, signature)) ); return (success && result.length >= 32 && abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.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); * } * ``` */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @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 IERC165 { /** * @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 v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @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 towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (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 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 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. uint256 twos = denominator & (0 - denominator); 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 (unsignedRoundsUp(rounding) && 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 * towards zero. * * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.20; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol) pragma solidity ^0.8.20; import {StorageSlot} from "./StorageSlot.sol"; // | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | // | length | 0x BB | type ShortString is bytes32; /** * @dev This library provides functions to convert short memory strings * into a `ShortString` type that can be used as an immutable variable. * * Strings of arbitrary length can be optimized using this library if * they are short enough (up to 31 bytes) by packing them with their * length (1 byte) in a single EVM word (32 bytes). Additionally, a * fallback mechanism can be used for every other case. * * Usage example: * * ```solidity * contract Named { * using ShortStrings for *; * * ShortString private immutable _name; * string private _nameFallback; * * constructor(string memory contractName) { * _name = contractName.toShortStringWithFallback(_nameFallback); * } * * function name() external view returns (string memory) { * return _name.toStringWithFallback(_nameFallback); * } * } * ``` */ library ShortStrings { // Used as an identifier for strings longer than 31 bytes. bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF; error StringTooLong(string str); error InvalidShortString(); /** * @dev Encode a string of at most 31 chars into a `ShortString`. * * This will trigger a `StringTooLong` error is the input string is too long. */ function toShortString(string memory str) internal pure returns (ShortString) { bytes memory bstr = bytes(str); if (bstr.length > 31) { revert StringTooLong(str); } return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length)); } /** * @dev Decode a `ShortString` back to a "normal" string. */ function toString(ShortString sstr) internal pure returns (string memory) { uint256 len = byteLength(sstr); // using `new string(len)` would work locally but is not memory safe. string memory str = new string(32); /// @solidity memory-safe-assembly assembly { mstore(str, len) mstore(add(str, 0x20), sstr) } return str; } /** * @dev Return the length of a `ShortString`. */ function byteLength(ShortString sstr) internal pure returns (uint256) { uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF; if (result > 31) { revert InvalidShortString(); } return result; } /** * @dev Encode a string into a `ShortString`, or write it to storage if it is too long. */ function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) { if (bytes(value).length < 32) { return toShortString(value); } else { StorageSlot.getStringSlot(store).value = value; return ShortString.wrap(FALLBACK_SENTINEL); } } /** * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}. */ function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) { if (ShortString.unwrap(value) != FALLBACK_SENTINEL) { return toString(value); } else { return store; } } /** * @dev Return the length of a string that was encoded to `ShortString` or written to storage using * {setWithFallback}. * * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of * actual characters as the UTF-8 encoding of a single character can span over multiple bytes. */ function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) { if (ShortString.unwrap(value) != FALLBACK_SENTINEL) { return byteLength(value); } else { return bytes(store).length; } } }
// 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: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol) pragma solidity ^0.8.20; import {Math} from "./math/Math.sol"; import {SignedMath} from "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; /** * @dev The `value` string doesn't fit in the specified `length`. */ error StringsInsufficientHexLength(uint256 value, uint256 length); /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.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), HEX_DIGITS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toStringSigned(int256 value) internal pure returns (string memory) { return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.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) { uint256 localValue = value; 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] = HEX_DIGITS[localValue & 0xf]; localValue >>= 4; } if (localValue != 0) { revert StringsInsufficientHexLength(value, length); } 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); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {NODL} from "../NODL.sol"; /// @title BridgeBase /// @notice Abstract contract for bridging free or vested tokens with the help of oracles. /// @dev This contract provides basic functionalities for voting on proposals /// to bridge tokens and ensuring certain constraints such as voting thresholds and delays. abstract contract BridgeBase { /// @notice Token contract address for the NODL token. NODL public immutable nodl; /// @notice Mapping to track whether an address is an oracle. mapping(address => bool) public isOracle; /// @notice Minimum number of votes needed to execute a proposal. uint8 public threshold; /// @notice Number of blocks to delay before a proposal can be executed after reaching the voting threshold. uint256 public delay; /// @notice Maximum number of oracles allowed. uint8 public constant MAX_ORACLES = 10; /// @notice Mapping of proposals to oracles votes on them. mapping(bytes32 => mapping(address => bool)) public voted; /// @notice Emitted when the first vote a proposal has been cast. event VoteStarted(bytes32 indexed proposal, address oracle, address indexed user, uint256 amount); /// @notice Emitted when an oracle votes on a proposal which is already created. event Voted(bytes32 indexed proposal, address oracle); /// @notice Error to indicate an oracle has already voted on a proposal. error AlreadyVoted(bytes32 proposal, address oracle); /// @notice Error to indicate a proposal has already been executed. error AlreadyExecuted(bytes32 proposal); /// @notice Error to indicate parameters of a proposal have been changed after initiation. error ParametersChanged(bytes32 proposal); /// @notice Error to indicate an address is not recognized as an oracle. error NotAnOracle(address user); /// @notice Error to indicate it's too soon to execute a proposal. /// Please note `withdraw` here refers to a function for free tokens where they are minted for the user. /// We have kept the name for API compatibility. error NotYetWithdrawable(bytes32 proposal); /// @notice Error to indicate insufficient votes to execute a proposal. error NotEnoughVotes(bytes32 proposal); /// @notice Error to indicate that the number of oracles exceeds the allowed maximum. error MaxOraclesExceeded(); /// @notice Error to indicate that the number of oracles is less than the minimum required. error NotEnoughOracles(); /// @notice Error to indicate an invalid value for the minimum votes. error InvalidZeroForMinVotes(); /// @notice Initializes the contract with specified parameters. /// @param bridgeOracles Array of oracle accounts. /// @param token Contract address of the NODL token. /// @param minVotes Minimum required votes to consider a proposal valid. /// @param minDelay Blocks to wait before a passed proposal can be executed. constructor(address[] memory bridgeOracles, NODL token, uint8 minVotes, uint256 minDelay) { _mustNotBeZeroMinVotes(minVotes); _mustHaveEnoughOracles(bridgeOracles.length, minVotes); _mustNotExceedMaxOracles(bridgeOracles.length); for (uint256 i = 0; i < bridgeOracles.length; i++) { isOracle[bridgeOracles[i]] = true; } nodl = token; threshold = minVotes; delay = minDelay; } /// @notice Creates a new vote on a proposal. /// @dev Sets initial values and emits a VoteStarted event. /// @param proposal The hash identifier of the proposal. /// @param oracle The oracle address initiating the vote. /// @param user The user address associated with the vote. /// @param amount The amount of tokens being bridged. function _createVote(bytes32 proposal, address oracle, address user, uint256 amount) internal virtual { _processVote(proposal, oracle); emit VoteStarted(proposal, oracle, user, amount); } /// @notice Records a vote for a proposal by an oracle. /// @param proposal The hash identifier of the proposal. /// @param oracle The oracle casting the vote. function _recordVote(bytes32 proposal, address oracle) internal virtual { _mustNotHaveVotedYet(proposal, oracle); _processVote(proposal, oracle); emit Voted(proposal, oracle); } /// @notice Processes a vote for a proposal. function _processVote(bytes32 proposal, address oracle) internal virtual { voted[proposal][oracle] = true; _incTotalVotes(proposal); _updateLastVote(proposal, block.number); } /// @notice Executes a proposal after all conditions are met. /// @param proposal The hash identifier of the proposal. function _execute(bytes32 proposal) internal { _mustNotHaveExecutedYet(proposal); _mustHaveEnoughVotes(proposal); _mustBePastSafetyDelay(proposal); _flagAsExecuted(proposal); } /** * @dev Updates the last vote value for a given proposal. * @param proposal The identifier of the proposal. * @param value The new value for the last vote. */ function _updateLastVote(bytes32 proposal, uint256 value) internal virtual; /** * @dev Increments the total votes count for a given proposal. * @param proposal The identifier of the proposal. */ function _incTotalVotes(bytes32 proposal) internal virtual; /** * @dev Flags a proposal as executed. * @param proposal The identifier of the proposal. */ function _flagAsExecuted(bytes32 proposal) internal virtual; /** * @dev Retrieves the last vote value for a given proposal. * @param proposal The identifier of the proposal. * @return The last vote value. */ function _lastVote(bytes32 proposal) internal view virtual returns (uint256); /** * @dev Retrieves the total votes count for a given proposal. * @param proposal The identifier of the proposal. * @return The total votes count. */ function _totalVotes(bytes32 proposal) internal view virtual returns (uint8); /** * @dev Checks if a proposal has been executed. * @param proposal The identifier of the proposal. * @return A boolean indicating if the proposal has been executed. */ function _executed(bytes32 proposal) internal view virtual returns (bool); function _mustNotHaveExecutedYet(bytes32 proposal) internal view { if (_executed(proposal)) { revert AlreadyExecuted(proposal); } } function _mustBePastSafetyDelay(bytes32 proposal) internal view { if (block.number - _lastVote(proposal) < delay) { revert NotYetWithdrawable(proposal); } } function _mustHaveEnoughVotes(bytes32 proposal) internal view { if (_totalVotes(proposal) < threshold) { revert NotEnoughVotes(proposal); } } function _mustHaveEnoughOracles(uint256 length, uint256 minOracles) internal pure { if (length < minOracles) { revert NotEnoughOracles(); } } function _mustBeAnOracle(address maybeOracle) internal view { if (!isOracle[maybeOracle]) { revert NotAnOracle(maybeOracle); } } function _mustNotExceedMaxOracles(uint256 length) internal pure { if (length > MAX_ORACLES) { revert MaxOraclesExceeded(); } } function _mustNotBeZeroMinVotes(uint8 value) internal pure { if (value == 0) { revert InvalidZeroForMinVotes(); } } function _mustNotHaveVotedYet(bytes32 proposal, address oracle) internal view { if (voted[proposal][oracle]) { revert AlreadyVoted(proposal, oracle); } } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {Grants} from "../Grants.sol"; import {NODL} from "../NODL.sol"; import {BridgeBase} from "./BridgeBase.sol"; contract GrantsMigration is BridgeBase { /// @notice Maximum number of vesting schedules allowed per proposal. The 100 limit is coming from the Eden parachain. uint8 public constant MAX_SCHEDULES = 100; /// @dev Represents the vesting details of a proposal. struct Proposal { address target; // Address of the grant recipient. uint256 amount; // Total amount of tokens to be vested. Grants.VestingSchedule[] schedules; // Array of vesting schedules. } /// @dev Tracks voting and execution status of a proposal. struct ProposalStatus { uint256 lastVote; // Timestamp of the last vote. uint8 totalVotes; // Total number of votes cast. bool executed; // Whether the proposal has been executed. } // State variables Grants public immutable grants; // Grants contract. mapping(bytes32 => Proposal) public proposals; // Proposals identified by a hash. mapping(bytes32 => ProposalStatus) public proposalStatus; // Status of each proposal. // Events event Granted(bytes32 indexed proposal, address indexed user, uint256 amount, uint256 numOfSchedules); /** * @notice Error to indicate that the schedule array is empty. */ error EmptySchedules(); /** * @notice Error to indicate that the schedule array is too large. */ error TooManySchedules(); /** * @notice Error to indicate that the schedules do not add up to the total amount. */ error AmountMismatch(); /** * @param bridgeOracles Array of addresses authorized to initiate and vote on proposals. * @param token Address of the NODL token used for grants. * @param _grants Address of the Grants contract managing vesting schedules. * @param minVotes Minimum number of votes required to execute a proposal. * @param minDelay Minimum delay before a proposal can be executed. */ constructor(address[] memory bridgeOracles, NODL token, Grants _grants, uint8 minVotes, uint256 minDelay) BridgeBase(bridgeOracles, token, minVotes, minDelay) { grants = _grants; } /** * @notice Bridges a proposal for grant distribution across chains or domains. * @param paraTxHash Hash of the cross-chain transaction or parameter. * @param user Recipient of the grant. * @param amount Total token amount for the grant. * @param schedules Array of VestingSchedule, detailing the vesting mechanics. */ function bridge(bytes32 paraTxHash, address user, uint256 amount, Grants.VestingSchedule[] memory schedules) external { _mustBeAnOracle(msg.sender); _mustNotHaveExecutedYet(paraTxHash); if (_proposalExists(paraTxHash)) { _mustNotBeChangingParameters(paraTxHash, user, amount, schedules); _recordVote(paraTxHash, msg.sender); } else { _createProposal(paraTxHash, msg.sender, user, amount, schedules); } } /** * @notice Executes the grant proposal, transferring vested tokens according to the schedules. * @param paraTxHash Hash of the proposal to be executed. */ function grant(bytes32 paraTxHash) external { _execute(paraTxHash); Proposal storage p = proposals[paraTxHash]; nodl.mint(address(this), proposals[paraTxHash].amount); nodl.approve(address(grants), proposals[paraTxHash].amount); for (uint256 i = 0; i < p.schedules.length; i++) { grants.addVestingSchedule( p.target, p.schedules[i].start, p.schedules[i].period, p.schedules[i].periodCount, p.schedules[i].perPeriodAmount, p.schedules[i].cancelAuthority ); } emit Granted(paraTxHash, p.target, p.amount, p.schedules.length); } // Internal helper functions below function _mustNotBeChangingParameters( bytes32 proposal, address user, uint256 amount, Grants.VestingSchedule[] memory schedules ) internal view { Proposal storage storedProposal = proposals[proposal]; if (storedProposal.amount != amount || storedProposal.target != user) { revert ParametersChanged(proposal); } uint256 len = storedProposal.schedules.length; if (len != schedules.length) { revert ParametersChanged(proposal); } for (uint256 i = 0; i < len; i++) { if ( storedProposal.schedules[i].start != schedules[i].start || storedProposal.schedules[i].period != schedules[i].period || storedProposal.schedules[i].periodCount != schedules[i].periodCount || storedProposal.schedules[i].perPeriodAmount != schedules[i].perPeriodAmount || storedProposal.schedules[i].cancelAuthority != schedules[i].cancelAuthority ) { revert ParametersChanged(proposal); } } } function _createProposal( bytes32 proposal, address oracle, address user, uint256 amount, Grants.VestingSchedule[] memory schedules ) internal { if (schedules.length == 0) { revert EmptySchedules(); } if (schedules.length > MAX_SCHEDULES) { revert TooManySchedules(); } uint256 totalAmount = 0; for (uint256 i = 0; i < schedules.length; i++) { grants.validateVestingSchedule( user, schedules[i].period, schedules[i].periodCount, schedules[i].perPeriodAmount ); totalAmount += schedules[i].perPeriodAmount * schedules[i].periodCount; } if (totalAmount != amount) { revert AmountMismatch(); } proposals[proposal] = Proposal({target: user, amount: amount, schedules: schedules}); super._createVote(proposal, oracle, user, amount); } function _proposalExists(bytes32 proposal) internal view returns (bool) { return proposalStatus[proposal].totalVotes > 0; } function _flagAsExecuted(bytes32 proposal) internal override { proposalStatus[proposal].executed = true; } function _incTotalVotes(bytes32 proposal) internal override { proposalStatus[proposal].totalVotes++; } function _updateLastVote(bytes32 proposal, uint256 value) internal override { proposalStatus[proposal].lastVote = value; } function _totalVotes(bytes32 proposal) internal view override returns (uint8) { return proposalStatus[proposal].totalVotes; } function _lastVote(bytes32 proposal) internal view override returns (uint256) { return proposalStatus[proposal].lastVote; } function _executed(bytes32 proposal) internal view override returns (bool) { return proposalStatus[proposal].executed; } }
// SPDX-License-Identifier: BSD-3-Clause-Clear /* * NOTE: For anyone considering reusing this contract, we recommend reading through the * matter-labs audit results carefully before proceeding. The Nodle team will not address * this issue because we may not use this contract in the near future. * * NFT Minting Can Be Blocked * Severity: Medium * Status: Reported * * Users cannot mint NFTs once the `individualHolders` variable exceeds the `maxHolders` * value, which is enforced by _mustAlreadyBeHolderOrEnoughHoldersRemaining. To mint a * level-1 NFT, users will need to bridge a relatively small amount of NODL tokens compared * to other levels. Given that the `individualHolders` variable is shared among all NFT * levels, malicious actors could exploit this by bridging small amounts of NODL tokens to * multiple addresses until reaching the `maxHolders` limit and minting level-1 NFTs for * them. As a result, it will disrupt the minting process for legitimate users. * * Recommendation: * We recommend revising the NFT minting process to allow unlimited NFT minting for lower * levels. */ pragma solidity 0.8.23; import {ERC721} from "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol"; import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol"; import {NODLMigration} from "./NODLMigration.sol"; contract MigrationNFT is ERC721 { uint256 public nextTokenId; uint256 public immutable maxHolders; NODLMigration public immutable migration; uint256[] public levels; string[] public levelToTokenURI; error InvalidZeroHolders(); error InvalidZeroLevels(); /** * @notice Mapping of token IDs to the levels they represent denominated from 1 (0 means token does not exists) */ mapping(uint256 => uint256) public tokenIdToNextLevel; /** * @notice Mapping of holders to the highest level they reached (denominated from 1) */ mapping(address => uint256) public holderToNextLevel; uint256 public individualHolders; mapping(bytes32 => bool) public claimed; error UnsortedLevelsList(); error UnequalLengths(); error TooManyHolders(); error AlreadyClaimed(); error NoLevelUp(); error ProposalDoesNotExist(); error NotExecuted(); error SoulBoundIsNotTransferrable(); /** * @notice Construct a new MigrationNFT contract * @param _migration the NODLMigration contract to bridge tokens * @param _maxHolders the maximum number of holders for the NFTs * @param _levels an array representing the different reward levels expressed in * the amount of tokens needed to get the NFT * @param _levelToTokenURI an array of URIs to the metadata of the NFTs */ constructor( NODLMigration _migration, uint256 _maxHolders, uint256[] memory _levels, string[] memory _levelToTokenURI ) ERC721("OG ZK NODL", "OG_ZK_NODL") { if (_maxHolders == 0) { revert InvalidZeroHolders(); } if (_levels.length == 0) { revert InvalidZeroLevels(); } if (_levels.length != _levelToTokenURI.length) { revert UnequalLengths(); } for (uint256 i = 1; i < _levels.length; i++) { if (_levels[i] <= _levels[i - 1]) { revert UnsortedLevelsList(); } } migration = _migration; maxHolders = _maxHolders; levels = _levels; levelToTokenURI = _levelToTokenURI; } /** * @notice Return the URI of the proper metadata for the given token ID * @param tokenId the token ID to mint */ function tokenURI(uint256 tokenId) public view virtual override(ERC721) returns (string memory) { _requireOwned(tokenId); // this will also mean that tokenIdToNextLevel is at least 1 uint256 level = tokenIdToNextLevel[tokenId]; return levelToTokenURI[level - 1]; } /** * @notice Mint a new NFT for the given user * @param txHash the transaction hash to bridge */ function safeMint(bytes32 txHash) public { _mustNotHaveBeenClaimed(txHash); (address target, uint256 amount,,, bool executed) = migration.proposals(txHash); _mustBeAnExistingProposal(target); _mustBeExecuted(executed); bool alreadyHolder = _mustAlreadyBeHolderOrEnoughHoldersRemaining(target); (uint256[] memory levelsToMint, uint256 nbLevelsToMint) = _computeLevelUps(target, amount); claimed[txHash] = true; if (!alreadyHolder) { individualHolders++; } for (uint256 i = 0; i < nbLevelsToMint; i++) { uint256 tokenId = nextTokenId++; tokenIdToNextLevel[tokenId] = levelsToMint[i] + 1; holderToNextLevel[target] = levelsToMint[i] + 1; _safeMint(target, tokenId); } } function _computeLevelUps(address target, uint256 amount) internal view returns (uint256[] memory levelsToMint, uint256 nbLevelsToMint) { levelsToMint = new uint256[](levels.length); nbLevelsToMint = 0; // We effectively iterate over all the levels the `target` has YET // to qualify for. This expressively skips levels the `target` has // already qualified for. uint256 nextLevel = holderToNextLevel[target]; for (uint256 i = nextLevel; i < levels.length; i++) { if (amount >= levels[i]) { levelsToMint[i - nextLevel] = i; nbLevelsToMint++; } } if (nbLevelsToMint == 0) { revert NoLevelUp(); } } function _mustNotHaveBeenClaimed(bytes32 txHash) internal view { if (claimed[txHash]) { revert AlreadyClaimed(); } } function _mustBeAnExistingProposal(address target) internal pure { // the relayers skip any transfers to the 0 address if (target == address(0)) { revert ProposalDoesNotExist(); } } function _mustBeExecuted(bool executed) internal pure { if (!executed) { revert NotExecuted(); } } function _mustAlreadyBeHolderOrEnoughHoldersRemaining(address target) internal view returns (bool alreadyHolder) { alreadyHolder = balanceOf(target) > 0; if (!alreadyHolder && individualHolders == maxHolders) { revert TooManyHolders(); } } function _update(address to, uint256 tokenId, address auth) internal override(ERC721) returns (address) { address from = _ownerOf(tokenId); if (from != address(0) && to != address(0)) { // only burn or mint is allowed for a SoulBound token revert SoulBoundIsNotTransferrable(); } return super._update(to, tokenId, auth); } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {NODL} from "../NODL.sol"; import {BridgeBase} from "./BridgeBase.sol"; /// @title NODLMigration /// @notice This contract is used to help migrating the NODL assets from the Nodle Parachain /// to the ZkSync contracts. contract NODLMigration is BridgeBase { struct Proposal { address target; uint256 amount; uint256 lastVote; uint8 totalVotes; bool executed; } // We track votes in a separate mapping to avoid having to write helper functions to // expose the votes for each proposal. mapping(bytes32 => Proposal) public proposals; event Withdrawn(bytes32 indexed proposal, address indexed user, uint256 amount); constructor(address[] memory bridgeOracles, NODL token, uint8 minVotes, uint256 minDelay) BridgeBase(bridgeOracles, token, minVotes, minDelay) {} /// @notice Bridge some tokens from the Nodle Parachain to the ZkSync contracts. This /// tracks "votes" from each oracle and unlocks execution after a withdrawal delay. /// @param paraTxHash The transaction hash on the Parachain for this transfer. /// @param user The user address. /// @param amount The amount of NODL tokens that the user has burnt on the Parachain. function bridge(bytes32 paraTxHash, address user, uint256 amount) external { _mustBeAnOracle(msg.sender); _mustNotHaveExecutedYet(paraTxHash); if (_proposalExists(paraTxHash)) { _mustNotBeChangingParameters(paraTxHash, user, amount); _recordVote(paraTxHash, msg.sender); } else { _createVote(paraTxHash, msg.sender, user, amount); } } /// @notice Withdraw the NODL tokens from the contract to the user's address if the /// proposal has enough votes and has passed the safety delay. /// @param paraTxHash The transaction hash on the Parachain for this transfer. function withdraw(bytes32 paraTxHash) external { _execute(paraTxHash); _withdraw(paraTxHash, proposals[paraTxHash].target, proposals[paraTxHash].amount); } function _mustNotBeChangingParameters(bytes32 proposal, address user, uint256 amount) internal view { if (proposals[proposal].amount != amount || proposals[proposal].target != user) { revert ParametersChanged(proposal); } } function _proposalExists(bytes32 proposal) internal view returns (bool) { return proposals[proposal].totalVotes > 0 && proposals[proposal].amount > 0; } function _createVote(bytes32 proposal, address oracle, address user, uint256 amount) internal override { proposals[proposal].target = user; proposals[proposal].amount = amount; super._createVote(proposal, oracle, user, amount); } function _withdraw(bytes32 proposal, address user, uint256 amount) internal { nodl.mint(user, amount); emit Withdrawn(proposal, user, amount); } function _flagAsExecuted(bytes32 proposal) internal override { proposals[proposal].executed = true; } function _incTotalVotes(bytes32 proposal) internal override { proposals[proposal].totalVotes++; } function _updateLastVote(bytes32 proposal, uint256 value) internal override { proposals[proposal].lastVote = value; } function _totalVotes(bytes32 proposal) internal view override returns (uint8) { return proposals[proposal].totalVotes; } function _lastVote(bytes32 proposal) internal view override returns (uint256) { return proposals[proposal].lastVote; } function _executed(bytes32 proposal) internal view override returns (bool) { return proposals[proposal].executed; } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {ERC721} from "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol"; import {ERC721URIStorage} from "openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import {WhitelistPaymaster} from "../paymasters/WhitelistPaymaster.sol"; /// @notice a simple NFT contract for contentsign data where each nft is mapped to a one-time /// configurable URL. This is used for every variant of ContentSign with associated hooks. abstract contract BaseContentSign is ERC721, ERC721URIStorage { uint256 public nextTokenId; error UserIsNotWhitelisted(address user); constructor(string memory name, string memory symbol) ERC721(name, symbol) {} function safeMint(address to, string memory uri) public { _mustBeWhitelisted(); uint256 tokenId = nextTokenId++; _safeMint(to, tokenId); _setTokenURI(tokenId, uri); } function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); } function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721URIStorage) returns (bool) { return ERC721.supportsInterface(interfaceId) || ERC721URIStorage.supportsInterface(interfaceId); } function _mustBeWhitelisted() internal view { if (!_userIsWhitelisted(msg.sender)) { revert UserIsNotWhitelisted(msg.sender); } } function _userIsWhitelisted(address user) internal view virtual returns (bool); }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {BaseContentSign} from "./BaseContentSign.sol"; import {WhitelistPaymaster} from "../paymasters/WhitelistPaymaster.sol"; /// @notice the content sign contract variant for Click. Only users whitelisted on the paymaster can mint tokens contract ClickContentSign is BaseContentSign { WhitelistPaymaster public whitelistPaymaster; constructor(string memory name, string memory symbol, WhitelistPaymaster whitelist) BaseContentSign(name, symbol) { whitelistPaymaster = whitelist; } function _userIsWhitelisted(address user) internal view override returns (bool) { return whitelistPaymaster.isWhitelistedUser(user); } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {BaseContentSign} from "./BaseContentSign.sol"; import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol"; /// @notice the content sign contract variant for enterprises. Only users whitelisted on this contract can mint contract EnterpriseContentSign is BaseContentSign, AccessControl { bytes32 public constant WHITELISTED_ROLE = keccak256("WHITELISTED_ROLE"); constructor(string memory name, string memory symbol, address admin) BaseContentSign(name, symbol) { _grantRole(DEFAULT_ADMIN_ROLE, admin); } function supportsInterface(bytes4 interfaceId) public view override(BaseContentSign, AccessControl) returns (bool) { return BaseContentSign.supportsInterface(interfaceId) || AccessControl.supportsInterface(interfaceId); } function _userIsWhitelisted(address user) internal view override returns (bool) { return hasRole(WHITELISTED_ROLE, user); } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import {ERC20Burnable} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol"; contract NODL is ERC20Burnable, AccessControl { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); constructor(address admin) ERC20("Nodle Token", "NODL") { _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(MINTER_ROLE, admin); } function mint(address to, uint256 amount) public { _checkRole(MINTER_ROLE); _mint(to, amount); } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import { IPaymaster, ExecutionResult, PAYMASTER_VALIDATION_SUCCESS_MAGIC } from "zksync-contracts/l2/system-contracts/interfaces/IPaymaster.sol"; import {IPaymasterFlow} from "zksync-contracts/l2/system-contracts/interfaces/IPaymasterFlow.sol"; import {Transaction} from "zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import {BOOTLOADER_FORMAL_ADDRESS} from "zksync-contracts/l2/system-contracts/Constants.sol"; import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol"; /// @notice This smart contract serves as a base for any other paymaster contract. abstract contract BasePaymaster is IPaymaster, AccessControl { bytes32 public constant WITHDRAWER_ROLE = keccak256("WITHDRAWER_ROLE"); /** * @notice Emitted when the paymaster withdraws funds to the specified address. * @param amount The amount of funds withdrawn. */ event Withdrawn(address to, uint256 amount); error AccessRestrictedToBootloader(); error PaymasterFlowNotSupported(); error NotEnoughETHInPaymasterToPayForTransaction(); error InvalidPaymasterInput(string message); error FailedToWithdraw(); constructor(address admin, address withdrawer) { _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(WITHDRAWER_ROLE, withdrawer); } function validateAndPayForPaymasterTransaction(bytes32, bytes32, Transaction calldata transaction) external payable returns (bytes4 magic, bytes memory /* context */ ) { _mustBeBootloader(); // By default we consider the transaction as accepted. magic = PAYMASTER_VALIDATION_SUCCESS_MAGIC; if (transaction.paymasterInput.length < 4) { revert InvalidPaymasterInput("The standard paymaster input must be at least 4 bytes long"); } bytes4 paymasterInputSelector = bytes4(transaction.paymasterInput[0:4]); // Note, that while the minimal amount of ETH needed is tx.gasPrice * tx.gasLimit, // neither paymaster nor account are allowed to access this context variable. uint256 requiredETH = transaction.gasLimit * transaction.maxFeePerGas; address destAddress = address(uint160(transaction.to)); address userAddress = address(uint160(transaction.from)); if (paymasterInputSelector == IPaymasterFlow.general.selector) { _validateAndPayGeneralFlow(userAddress, destAddress, requiredETH); } else if (paymasterInputSelector == IPaymasterFlow.approvalBased.selector) { (address token, uint256 minimalAllowance, bytes memory data) = abi.decode(transaction.paymasterInput[4:], (address, uint256, bytes)); _validateAndPayApprovalBasedFlow(userAddress, destAddress, token, minimalAllowance, data, requiredETH); } else { revert PaymasterFlowNotSupported(); } // The bootloader never returns any data, so it can safely be ignored here. (bool success,) = payable(BOOTLOADER_FORMAL_ADDRESS).call{value: requiredETH}(""); if (!success) { revert NotEnoughETHInPaymasterToPayForTransaction(); } return (magic, ""); } function postTransaction(bytes calldata, Transaction calldata, bytes32, bytes32, ExecutionResult, uint256) external payable override { _mustBeBootloader(); // Refunds are not supported yet. } function withdraw(address to, uint256 amount) external { _checkRole(WITHDRAWER_ROLE); (bool success,) = payable(to).call{value: amount}(""); if (!success) revert FailedToWithdraw(); emit Withdrawn(to, amount); } receive() external payable {} function _mustBeBootloader() internal view { if (msg.sender != BOOTLOADER_FORMAL_ADDRESS) { revert AccessRestrictedToBootloader(); } } function _validateAndPayGeneralFlow(address from, address to, uint256 requiredETH) internal virtual; function _validateAndPayApprovalBasedFlow( address from, address to, address token, uint256 tokenAmount, bytes memory data, uint256 requiredETH ) internal virtual; }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {BasePaymaster} from "./BasePaymaster.sol"; /// @notice a paymaster that allow whitelisted users to do free txs to restricted contracts contract WhitelistPaymaster is BasePaymaster { bytes32 public constant WHITELIST_ADMIN_ROLE = keccak256("WHITELIST_ADMIN_ROLE"); mapping(address => bool) public isWhitelistedUser; mapping(address => bool) public isWhitelistedContract; event WhitelistedContractsAdded(address[] contracts); event WhitelistedContractsRemoved(address[] contracts); event WhitelistedUsersAdded(address[] users); event WhitelistedUsersRemoved(address[] users); error UserIsNotWhitelisted(); error DestIsNotWhitelisted(); constructor(address admin, address withdrawer) BasePaymaster(admin, withdrawer) { _grantRole(WHITELIST_ADMIN_ROLE, admin); } function addWhitelistedContracts(address[] calldata whitelistedContracts) external { _checkRole(WHITELIST_ADMIN_ROLE); for (uint256 i = 0; i < whitelistedContracts.length; i++) { isWhitelistedContract[whitelistedContracts[i]] = true; } emit WhitelistedContractsAdded(whitelistedContracts); } function removeWhitelistedContracts(address[] calldata whitelistedContracts) external { _checkRole(WHITELIST_ADMIN_ROLE); for (uint256 i = 0; i < whitelistedContracts.length; i++) { isWhitelistedContract[whitelistedContracts[i]] = false; } emit WhitelistedContractsRemoved(whitelistedContracts); } function addWhitelistedUsers(address[] calldata users) external { _checkRole(WHITELIST_ADMIN_ROLE); for (uint256 i = 0; i < users.length; i++) { isWhitelistedUser[users[i]] = true; } emit WhitelistedUsersAdded(users); } function removeWhitelistedUsers(address[] calldata users) external { _checkRole(WHITELIST_ADMIN_ROLE); for (uint256 i = 0; i < users.length; i++) { isWhitelistedUser[users[i]] = false; } emit WhitelistedUsersRemoved(users); } function _validateAndPayGeneralFlow(address from, address to, uint256 /* requiredETH */ ) internal view override { if (!isWhitelistedContract[to]) { revert DestIsNotWhitelisted(); } if (!isWhitelistedUser[from]) { revert UserIsNotWhitelisted(); } } function _validateAndPayApprovalBasedFlow(address, address, address, uint256, bytes memory, uint256) internal pure override { revert PaymasterFlowNotSupported(); } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {QuotaControl} from "./QuotaControl.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @title Payment Contract * @dev A contract that enables secure payments and withdrawals using ERC20 tokens. * The contract is controlled by an Oracle role and incorporates quota control * to manage payments within a specified limit. * * Inherits from `QuotaControl` to manage payment quotas. */ contract Payment is QuotaControl { using SafeERC20 for IERC20; /// @dev Role identifier for the Oracle. Accounts with this role can trigger payments. bytes32 public constant ORACLE_ROLE = keccak256("ORACLE_ROLE"); /// @dev ERC20 token used for payments in this contract. IERC20 public immutable token; /** * @dev Error emitted when an attempt to pay fails due to insufficient contract balance. * @param balance The current token balance of the contract. * @param needed The minimum token amount required to execute the payment. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev Constructor that initializes the contract with the provided parameters. * @param oracle The address of the account assigned the `ORACLE_ROLE` to manage payments. * @param _token The address of the ERC20 token contract used for payments. * @param initialQuota The initial quota limit for payments. * @param initialPeriod The initial time period for which the quota is valid. * @param admin The address assigned the `DEFAULT_ADMIN_ROLE` for administrative privileges. */ constructor(address oracle, address _token, uint256 initialQuota, uint256 initialPeriod, address admin) QuotaControl(initialQuota, initialPeriod, admin) { _grantRole(ORACLE_ROLE, oracle); // Grant ORACLE_ROLE to the specified oracle address. token = IERC20(_token); // Set the ERC20 token used for payments. } /** * @notice Pays a specified amount to a list of recipients. * @dev Can only be called by accounts with the `ORACLE_ROLE`. * The total required amount is calculated by multiplying the number of recipients by the specified amount. * If the contract's token balance is insufficient, the transaction reverts with `InsufficientBalance`. * * @param recipients An array of addresses to receive the payments. * @param amount The amount of tokens to be paid to each recipient. * * Emits a `Transfer` event from the token for each recipient. */ function pay(address[] calldata recipients, uint256 amount) external onlyRole(ORACLE_ROLE) { uint256 needed = recipients.length * amount; // Calculate the total tokens required. uint256 balance = token.balanceOf(address(this)); // Get the current balance of the contract. if (balance < needed) { revert InsufficientBalance(balance, needed); } _checkedResetClaimed(); _checkedUpdateClaimed(needed); for (uint256 i = 0; i < recipients.length; i++) { token.safeTransfer(recipients[i], amount); } } /** * @notice Withdraws a specified amount of tokens to the provided recipient address. * @dev Can only be called by accounts with the `DEFAULT_ADMIN_ROLE`. * * @param recipient The address to receive the withdrawn tokens. * @param amount The amount of tokens to be transferred to the recipient. * * Emits a `Transfer` event from the token to the recipient. */ function withdraw(address recipient, uint256 amount) external onlyRole(DEFAULT_ADMIN_ROLE) { token.safeTransfer(recipient, amount); } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol"; import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol"; /** * @title QuotaControl * @dev A contract designed to manage and control periodic quotas, such as token distributions or other `uint256`-based allowances. * It ensures that the total claims or usage within a given period do not exceed the predefined quota, thus regulating the flow of resources. * * The contract employs a time-based system where the quota is automatically reset at the start of each period. * An administrator, holding the `DEFAULT_ADMIN_ROLE`, has the authority to set and modify both the quota and the renewal period. * * Key Features: * - Prevents claims that exceed the current quota. * - Allows the admin to update the quota and renewal period. * - Automatically resets the claimed amount when a new period begins. */ contract QuotaControl is AccessControl { using Math for uint256; /** * @dev The maximum allowable period for reward quota renewal. This limit prevents potential overflows and reduces the need for safe math checks. */ uint256 public constant MAX_PERIOD = 30 days; /** * @dev The current period for reward quota renewal, specified in seconds. */ uint256 public period; /** * @dev The maximum amount of rewards that can be distributed in the current period. */ uint256 public quota; /** * @dev The timestamp when the reward quota will be renewed next. */ uint256 public quotaRenewalTimestamp; /** * @dev The total amount of rewards claimed within the current period. */ uint256 public claimed; /** * @dev Error triggered when the claim amount exceeds the current reward quota. */ error QuotaExceeded(); /** * @dev Error triggered when the period for reward renewal is set to zero. */ error ZeroPeriod(); /** * @dev Error triggered when the set period exceeds the maximum allowable period. */ error TooLongPeriod(); /** * @dev Emitted when the reward quota is updated. * @param quota The new reward quota. */ event QuotaSet(uint256 quota); /** * @dev Emitted when the reward period is updated. * @param period The new reward period in seconds. */ event PeriodSet(uint256 period); /** * @dev Initializes the contract with an initial reward quota, reward period, and admin. * @param initialQuota The initial maximum amount of rewards distributable in each period. * @param initialPeriod The initial duration of the reward period in seconds. * @param admin The address granted the `DEFAULT_ADMIN_ROLE`, responsible for updating contract settings. * * Requirements: * - `initialPeriod` must be within the acceptable range (greater than 0 and less than or equal to `MAX_PERIOD`). */ constructor(uint256 initialQuota, uint256 initialPeriod, address admin) { _mustBeWithinPeriodRange(initialPeriod); _grantRole(DEFAULT_ADMIN_ROLE, admin); quota = initialQuota; period = initialPeriod; quotaRenewalTimestamp = block.timestamp + period; } /** * @dev Sets a new reward quota. Can only be called by an account with the `DEFAULT_ADMIN_ROLE`. * @param newQuota The new maximum amount of rewards distributable in each period. */ function setQuota(uint256 newQuota) external onlyRole(DEFAULT_ADMIN_ROLE) { quota = newQuota; emit QuotaSet(newQuota); } /** * @dev Sets a new reward period. Can only be called by an account with the `DEFAULT_ADMIN_ROLE`. * @param newPeriod The new duration of the reward period in seconds. * * Requirements: * - `newPeriod` must be greater than 0 and less than or equal to `MAX_PERIOD`. */ function setPeriod(uint256 newPeriod) external onlyRole(DEFAULT_ADMIN_ROLE) { _mustBeWithinPeriodRange(newPeriod); period = newPeriod; emit PeriodSet(newPeriod); } /** * @dev Internal function that resets the claimed rewards to 0 and updates the next quota renewal timestamp. * If the current timestamp is beyond the quota renewal timestamp, a new period begins. * * The reset calculation ensures that the renewal timestamp will always be aligned with the period's duration, even if time has passed beyond the expected renewal time. */ function _checkedResetClaimed() internal { if (block.timestamp >= quotaRenewalTimestamp) { claimed = 0; // Align the quota renewal timestamp to the next period start uint256 timeAhead = block.timestamp - quotaRenewalTimestamp; quotaRenewalTimestamp = block.timestamp + period - (timeAhead % period); } } /** * @dev Internal function to update the claimed rewards by a specified amount. * * If the total claimed amount exceeds the quota, the transaction is reverted. * @param amount The amount of rewards being claimed. * * Requirements: * - The updated `claimed` amount must not exceed the current reward quota. */ function _checkedUpdateClaimed(uint256 amount) internal { (bool success, uint256 newClaimed) = claimed.tryAdd(amount); if (!success || newClaimed > quota) { revert QuotaExceeded(); } claimed = newClaimed; } /** * @dev Internal function to validate that the provided reward period is within the allowed range. * @param newPeriod The period to validate. * * Requirements: * - The `newPeriod` must be greater than 0. * - The `newPeriod` must not exceed `MAX_PERIOD`. */ function _mustBeWithinPeriodRange(uint256 newPeriod) internal { if (newPeriod == 0) { revert ZeroPeriod(); } if (newPeriod > MAX_PERIOD) { revert TooLongPeriod(); } } }
// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity 0.8.23; import {NODL} from "./NODL.sol"; import {QuotaControl} from "./QuotaControl.sol"; import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol"; import {EIP712} from "openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol"; import {SignatureChecker} from "openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sol"; import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol"; /** * @title Nodle DePIN Rewards * @dev This contract allows an authorized oracle to issue off-chain signed rewards to recipients. * This contract must have the MINTER_ROLE in the NODL token contract. */ contract Rewards is QuotaControl, EIP712 { using Math for uint256; /** * @dev The signing domain used for generating signatures. */ string public constant SIGNING_DOMAIN = "rewards.depin.nodle"; /** * @dev The version of the signature scheme used. */ string public constant SIGNATURE_VERSION = "1"; /** * @dev The hash of the reward type structure. * It is calculated using the keccak256 hash function. * The structure consists of the recipient's address, the amount of the reward, and the sequence number for that recipient. */ bytes32 public constant REWARD_TYPE_HASH = keccak256("Reward(address recipient,uint256 amount,uint256 sequence)"); /** * @dev The hash of the batch reward type. * It is calculated by taking the keccak256 hash of the string representation of the batch reward type. * The batch reward type consists of the recipients, amounts, and sequence of that batch. */ bytes32 public constant BATCH_REWARD_TYPE_HASH = keccak256("BatchReward(bytes32 recipientsHash,bytes32 amountsHash,uint256 sequence)"); /** * @dev The maximum value for basis points values. */ uint16 public constant BASIS_POINTS_DIVISOR = 10000; /** * @dev Struct on which basis an individual reward must be issued. */ struct Reward { address recipient; uint256 amount; uint256 sequence; } /** * @dev Represents a batch reward distribution. * Each batch reward consists of an array of recipients, an array of corresponding amounts, * and a sequence number to avoid replay attacks. */ struct BatchReward { address[] recipients; uint256[] amounts; uint256 sequence; } /** * @dev Reference to the NODL token contract. */ NODL public immutable nodl; /** * @dev Address of the authorized oracle. */ address public immutable authorizedOracle; /** * @dev Mapping to store reward sequences for each recipient to prevent replay attacks. */ mapping(address => uint256) public sequences; /** * @dev The sequence number of the batch reward. */ uint256 public batchSequence; /** * @dev Represents the latest batch reward digest. */ bytes32 public latestBatchRewardDigest; /** * @notice The fraction of the batch reward that the submitter will receive. */ uint16 public batchSubmitterRewardBasisPoints; /** * @dev Error when the reward is not from the authorized oracle. */ error UnauthorizedOracle(); /** * @dev Error when the recipient's reward sequence does not match. */ error InvalidRecipientSequence(); /** * @dev Error thrown when an invalid batch sequence is encountered. */ error InvalidBatchSequence(); /** * @dev Throws an error if the batch structure is invalid. * The recipient and amounts arrays must have the same length. */ error InvalidBatchStructure(); /** * @dev Error thrown when the value is out of range. For example for basis point values they should be less than BASIS_POINTS_DIVISOR. */ error OutOfRangeValue(); /** * @dev Event emitted when the submitter's reward basis point is set. */ event BatchSubmitterRewardSet(uint16 bp); /** * @dev Event emitted when a reward is minted. */ event Minted(address indexed recipient, uint256 amount, uint256 totalRewardsClaimed); /** * @dev Emitted when a batch reward is minted. * @param batchSum The sum of rewards in the batch. * @param totalRewardsClaimed The total number of rewards claimed so far. */ event BatchMinted(uint256 batchSum, uint256 totalRewardsClaimed, bytes32 digest); /** * @dev Initializes the contract with the specified parameters. * @param token Address of the NODL token contract. * @param initialQuota Initial reward quota. * @param initialPeriod Initial reward period. * @param oracleAddress Address of the authorized oracle. * @param rewardBasisPoints The fraction of the reward to be given to the batch submitter. * @param admin Address of the admin who can change parameters like quota, period and submitter's reward fraction. */ constructor( NODL token, uint256 initialQuota, uint256 initialPeriod, address oracleAddress, uint16 rewardBasisPoints, address admin ) QuotaControl(initialQuota, initialPeriod, admin) EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) { _mustBeLessThanBasisPointsDivisor(rewardBasisPoints); nodl = token; authorizedOracle = oracleAddress; batchSubmitterRewardBasisPoints = rewardBasisPoints; } /** * @dev Mints rewards to the recipient if the signature is valid and quota is not exceeded. * @param reward The reward details. * @param signature The signature from the authorized oracle. */ function mintReward(Reward memory reward, bytes memory signature) external { _mustBeExpectedSequence(reward.recipient, reward.sequence); _mustBeFromAuthorizedOracle(digestReward(reward), signature); _checkedResetClaimed(); _checkedUpdateClaimed(reward.amount); // Safe to increment the sequence after checking this is the expected number (no overflow for the age of universe even with 1000 reward claims per second) sequences[reward.recipient] = reward.sequence + 1; nodl.mint(reward.recipient, reward.amount); emit Minted(reward.recipient, reward.amount, claimed); } /** * @dev Mints batch rewards to multiple recipients. * @param batch The BatchReward struct containing the recipients and amounts of rewards to be minted. * @param signature The signature to verify the authenticity of the batch reward. */ function mintBatchReward(BatchReward memory batch, bytes memory signature) external { _mustBeValidBatchStructure(batch); _mustBeExpectedBatchSequence(batch.sequence); bytes32 digest = digestBatchReward(batch); _mustBeFromAuthorizedOracle(digest, signature); _checkedResetClaimed(); uint256 batchSum = _batchSum(batch); uint256 submitterRewardAmount = (batchSum * batchSubmitterRewardBasisPoints) / BASIS_POINTS_DIVISOR; _checkedUpdateClaimed(batchSum + submitterRewardAmount); // Safe to increment the sequence after checking this is the expected number (no overflow for the age of universe even with 1000 reward claims per second) batchSequence = batch.sequence + 1; latestBatchRewardDigest = digest; for (uint256 i = 0; i < batch.recipients.length; i++) { nodl.mint(batch.recipients[i], batch.amounts[i]); } nodl.mint(msg.sender, submitterRewardAmount); emit BatchMinted(batchSum, claimed, digest); } /** * @dev Sets the fraction of the batch reward that the submitter will receive. * @param newBasisPoints The new basis points value. */ function setBatchSubmitterRewardBasisPoints(uint16 newBasisPoints) external { _checkRole(DEFAULT_ADMIN_ROLE); _mustBeLessThanBasisPointsDivisor(newBasisPoints); batchSubmitterRewardBasisPoints = newBasisPoints; emit BatchSubmitterRewardSet(newBasisPoints); } /** * @dev Internal check to ensure the basis points value is less than the divisor. * @param basisPoints The basis points value to be checked. */ function _mustBeLessThanBasisPointsDivisor(uint16 basisPoints) internal pure { if (basisPoints > BASIS_POINTS_DIVISOR) { revert OutOfRangeValue(); } } /** * @dev Internal check to ensure the `sequence` value is expected for `recipient`. * @param recipient The address of the recipient to check. * @param sequence The sequence value. */ function _mustBeExpectedSequence(address recipient, uint256 sequence) internal view { if (sequences[recipient] != sequence) { revert InvalidRecipientSequence(); } } /** * @dev Internal checks to ensure the given sequence is expected for the batch. * @param sequence The sequence to be checked. */ function _mustBeExpectedBatchSequence(uint256 sequence) internal view { if (batchSequence != sequence) { revert InvalidBatchSequence(); } } /** * @dev Internal check to ensure the given batch reward structure is valid, meaning same number of recipients and amounts. * @param batch The batch reward structure to be validated. */ function _mustBeValidBatchStructure(BatchReward memory batch) internal pure { if (batch.recipients.length != batch.amounts.length) { revert InvalidBatchStructure(); } } /** * @dev Checks if the provided signature is valid for the given hash and authorized oracle address. * @param hash The hash to be verified. * @param signature The signature to be checked. * @dev Throws an `UnauthorizedOracle` exception if the signature is not valid. */ function _mustBeFromAuthorizedOracle(bytes32 hash, bytes memory signature) internal view { if (!SignatureChecker.isValidSignatureNow(authorizedOracle, hash, signature)) { revert UnauthorizedOracle(); } } /** * @dev Calculates the sum of amounts in a BatchReward struct. * @param batch The BatchReward struct containing the amounts to be summed. * @return The sum of all amounts in the batch. */ function _batchSum(BatchReward memory batch) internal pure returns (uint256) { uint256 sum = 0; for (uint256 i = 0; i < batch.amounts.length; i++) { sum += batch.amounts[i]; } return sum; } /** * @dev Helper function to get the digest of the typed data to be signed. * @param reward detailing recipient, amount, and sequence. * @return The hash of the typed data. */ function digestReward(Reward memory reward) public view returns (bytes32) { return _hashTypedDataV4(keccak256(abi.encode(REWARD_TYPE_HASH, reward.recipient, reward.amount, reward.sequence))); } /** * @dev Calculates the digest of a BatchReward struct. * @param batch The BatchReward struct containing the recipients, amounts, and sequence. * @return The digest of the BatchReward struct. */ function digestBatchReward(BatchReward memory batch) public view returns (bytes32) { bytes32 recipientsHash = keccak256(abi.encodePacked(batch.recipients)); bytes32 amountsHash = keccak256(abi.encodePacked(batch.amounts)); return _hashTypedDataV4(keccak256(abi.encode(BATCH_REWARD_TYPE_HASH, recipientsHash, amountsHash, batch.sequence))); } /** * @dev Returns the latest batch details. * @return The next batch sequence and the latest digest of a successfully submitted batch which must have been for batchSequence - 1. */ function latestBatchDetails() external view returns (uint256, bytes32) { return (batchSequence, latestBatchRewardDigest); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/IBootloaderUtilities.sol"; import "./libraries/TransactionHelper.sol"; import "./libraries/RLPEncoder.sol"; import "./libraries/EfficientCall.sol"; /** * @author Matter Labs * @notice A contract that provides some utility methods for the bootloader * that is very hard to write in Yul. */ contract BootloaderUtilities is IBootloaderUtilities { using TransactionHelper for *; /// @notice Calculates the canonical transaction hash and the recommended transaction hash. /// @param _transaction The transaction. /// @return txHash and signedTxHash of the transaction, i.e. the transaction hash to be used in the explorer and commits to all /// the fields of the transaction and the recommended hash to be signed for this transaction. /// @dev txHash must be unique for all transactions. function getTransactionHashes( Transaction calldata _transaction ) external view override returns (bytes32 txHash, bytes32 signedTxHash) { signedTxHash = _transaction.encodeHash(); if (_transaction.txType == EIP_712_TX_TYPE) { txHash = keccak256(bytes.concat(signedTxHash, EfficientCall.keccak(_transaction.signature))); } else if (_transaction.txType == LEGACY_TX_TYPE) { txHash = encodeLegacyTransactionHash(_transaction); } else if (_transaction.txType == EIP_1559_TX_TYPE) { txHash = encodeEIP1559TransactionHash(_transaction); } else if (_transaction.txType == EIP_2930_TX_TYPE) { txHash = encodeEIP2930TransactionHash(_transaction); } else { revert("Unsupported tx type"); } } /// @notice Calculates the hash for a legacy transaction. /// @param _transaction The legacy transaction. /// @return txHash The hash of the transaction. function encodeLegacyTransactionHash(Transaction calldata _transaction) internal view returns (bytes32 txHash) { // Hash of legacy transactions are encoded as one of the: // - RLP(nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0) // - RLP(nonce, gasPrice, gasLimit, to, value, data) // // In this RLP encoding, only the first one above list appears, so we encode each element // inside list and then concatenate the length of all elements with them. bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); // Encode `gasPrice` and `gasLimit` together to prevent "stack too deep error". bytes memory encodedGasParam; { bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); encodedGasParam = bytes.concat(encodedGasPrice, encodedGasLimit); } bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } bytes memory rEncoded; { uint256 rInt = uint256(bytes32(_transaction.signature[0:32])); rEncoded = RLPEncoder.encodeUint256(rInt); } bytes memory sEncoded; { uint256 sInt = uint256(bytes32(_transaction.signature[32:64])); sEncoded = RLPEncoder.encodeUint256(sInt); } bytes memory vEncoded; { uint256 vInt = uint256(uint8(_transaction.signature[64])); require(vInt == 27 || vInt == 28, "Invalid v value"); // If the `chainId` is specified in the transaction, then the `v` value is encoded as // `35 + y + 2 * chainId == vInt + 8 + 2 * chainId`, where y - parity bit (see EIP-155). if (_transaction.reserved[0] != 0) { vInt += 8 + block.chainid * 2; } vEncoded = RLPEncoder.encodeUint256(vInt); } bytes memory encodedListLength; unchecked { uint256 listLength = encodedNonce.length + encodedGasParam.length + encodedTo.length + encodedValue.length + encodedDataLength.length + _transaction.data.length + rEncoded.length + sEncoded.length + vEncoded.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( encodedListLength, encodedNonce, encodedGasParam, encodedTo, encodedValue, encodedDataLength, _transaction.data, vEncoded, rEncoded, sEncoded ) ); } /// @notice Calculates the hash for an EIP2930 transaction. /// @param _transaction The EIP2930 transaction. /// @return txHash The hash of the transaction. function encodeEIP2930TransactionHash(Transaction calldata _transaction) internal view returns (bytes32) { // Encode all fixed-length params to avoid "stack too deep error" bytes memory encodedFixedLengthParams; { bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); encodedFixedLengthParams = bytes.concat( encodedChainId, encodedNonce, encodedGasPrice, encodedGasLimit, encodedTo, encodedValue ); } // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // On zkSync, access lists are always zero length (at least for now). bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); bytes memory rEncoded; { uint256 rInt = uint256(bytes32(_transaction.signature[0:32])); rEncoded = RLPEncoder.encodeUint256(rInt); } bytes memory sEncoded; { uint256 sInt = uint256(bytes32(_transaction.signature[32:64])); sEncoded = RLPEncoder.encodeUint256(sInt); } bytes memory vEncoded; { uint256 vInt = uint256(uint8(_transaction.signature[64])); require(vInt == 27 || vInt == 28, "Invalid v value"); vEncoded = RLPEncoder.encodeUint256(vInt - 27); } bytes memory encodedListLength; unchecked { uint256 listLength = encodedFixedLengthParams.length + encodedDataLength.length + _transaction.data.length + encodedAccessListLength.length + rEncoded.length + sEncoded.length + vEncoded.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( "\x01", encodedListLength, encodedFixedLengthParams, encodedDataLength, _transaction.data, encodedAccessListLength, vEncoded, rEncoded, sEncoded ) ); } /// @notice Calculates the hash for an EIP1559 transaction. /// @param _transaction The legacy transaction. /// @return txHash The hash of the transaction. function encodeEIP1559TransactionHash(Transaction calldata _transaction) internal view returns (bytes32) { // The formula for hash of EIP1559 transaction in the original proposal: // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md // Encode all fixed-length params to avoid "stack too deep error" bytes memory encodedFixedLengthParams; { bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedMaxPriorityFeePerGas = RLPEncoder.encodeUint256(_transaction.maxPriorityFeePerGas); bytes memory encodedMaxFeePerGas = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); encodedFixedLengthParams = bytes.concat( encodedChainId, encodedNonce, encodedMaxPriorityFeePerGas, encodedMaxFeePerGas, encodedGasLimit, encodedTo, encodedValue ); } // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // On zkSync, access lists are always zero length (at least for now). bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); bytes memory rEncoded; { uint256 rInt = uint256(bytes32(_transaction.signature[0:32])); rEncoded = RLPEncoder.encodeUint256(rInt); } bytes memory sEncoded; { uint256 sInt = uint256(bytes32(_transaction.signature[32:64])); sEncoded = RLPEncoder.encodeUint256(sInt); } bytes memory vEncoded; { uint256 vInt = uint256(uint8(_transaction.signature[64])); require(vInt == 27 || vInt == 28, "Invalid v value"); vEncoded = RLPEncoder.encodeUint256(vInt - 27); } bytes memory encodedListLength; unchecked { uint256 listLength = encodedFixedLengthParams.length + encodedDataLength.length + _transaction.data.length + encodedAccessListLength.length + rEncoded.length + sEncoded.length + vEncoded.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( "\x02", encodedListLength, encodedFixedLengthParams, encodedDataLength, _transaction.data, encodedAccessListLength, vEncoded, rEncoded, sEncoded ) ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/IAccountCodeStorage.sol"; import "./interfaces/INonceHolder.sol"; import "./interfaces/IContractDeployer.sol"; import "./interfaces/IKnownCodesStorage.sol"; import "./interfaces/IImmutableSimulator.sol"; import "./interfaces/IEthToken.sol"; import "./interfaces/IL1Messenger.sol"; import "./interfaces/ISystemContext.sol"; import "./interfaces/IBytecodeCompressor.sol"; import "./BootloaderUtilities.sol"; /// @dev All the system contracts introduced by zkSync have their addresses /// started from 2^15 in order to avoid collision with Ethereum precompiles. uint160 constant SYSTEM_CONTRACTS_OFFSET = 0x8000; // 2^15 /// @dev All the system contracts must be located in the kernel space, /// i.e. their addresses must be below 2^16. uint160 constant MAX_SYSTEM_CONTRACT_ADDRESS = 0xffff; // 2^16 - 1 address constant ECRECOVER_SYSTEM_CONTRACT = address(0x01); address constant SHA256_SYSTEM_CONTRACT = address(0x02); /// @dev The current maximum deployed precompile address. /// Note: currently only two precompiles are deployed: /// 0x01 - ecrecover /// 0x02 - sha256 /// Important! So the constant should be updated if more precompiles are deployed. uint256 constant CURRENT_MAX_PRECOMPILE_ADDRESS = uint256(uint160(SHA256_SYSTEM_CONTRACT)); address payable constant BOOTLOADER_FORMAL_ADDRESS = payable(address(SYSTEM_CONTRACTS_OFFSET + 0x01)); IAccountCodeStorage constant ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT = IAccountCodeStorage( address(SYSTEM_CONTRACTS_OFFSET + 0x02) ); INonceHolder constant NONCE_HOLDER_SYSTEM_CONTRACT = INonceHolder(address(SYSTEM_CONTRACTS_OFFSET + 0x03)); IKnownCodesStorage constant KNOWN_CODE_STORAGE_CONTRACT = IKnownCodesStorage(address(SYSTEM_CONTRACTS_OFFSET + 0x04)); IImmutableSimulator constant IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT = IImmutableSimulator( address(SYSTEM_CONTRACTS_OFFSET + 0x05) ); IContractDeployer constant DEPLOYER_SYSTEM_CONTRACT = IContractDeployer(address(SYSTEM_CONTRACTS_OFFSET + 0x06)); // A contract that is allowed to deploy any codehash // on any address. To be used only during an upgrade. address constant FORCE_DEPLOYER = address(SYSTEM_CONTRACTS_OFFSET + 0x07); IL1Messenger constant L1_MESSENGER_CONTRACT = IL1Messenger(address(SYSTEM_CONTRACTS_OFFSET + 0x08)); address constant MSG_VALUE_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x09); IEthToken constant ETH_TOKEN_SYSTEM_CONTRACT = IEthToken(address(SYSTEM_CONTRACTS_OFFSET + 0x0a)); address constant KECCAK256_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x10); ISystemContext constant SYSTEM_CONTEXT_CONTRACT = ISystemContext(payable(address(SYSTEM_CONTRACTS_OFFSET + 0x0b))); BootloaderUtilities constant BOOTLOADER_UTILITIES = BootloaderUtilities(address(SYSTEM_CONTRACTS_OFFSET + 0x0c)); address constant EVENT_WRITER_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x0d); IBytecodeCompressor constant BYTECODE_COMPRESSOR_CONTRACT = IBytecodeCompressor( address(SYSTEM_CONTRACTS_OFFSET + 0x0e) ); /// @dev If the bitwise AND of the extraAbi[2] param when calling the MSG_VALUE_SIMULATOR /// is non-zero, the call will be assumed to be a system one. uint256 constant MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT = 1; /// @dev The maximal msg.value that context can have uint256 constant MAX_MSG_VALUE = 2 ** 128 - 1; /// @dev Prefix used during derivation of account addresses using CREATE2 /// @dev keccak256("zksyncCreate2") bytes32 constant CREATE2_PREFIX = 0x2020dba91b30cc0006188af794c2fb30dd8520db7e2c088b7fc7c103c00ca494; /// @dev Prefix used during derivation of account addresses using CREATE /// @dev keccak256("zksyncCreate") bytes32 constant CREATE_PREFIX = 0x63bae3a9951d38e8a3fbb7b70909afc1200610fc5bc55ade242f815974674f23;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IAccountCodeStorage { function storeAccountConstructingCodeHash(address _address, bytes32 _hash) external; function storeAccountConstructedCodeHash(address _address, bytes32 _hash) external; function markAccountCodeHashAsConstructed(address _address) external; function getRawCodeHash(address _address) external view returns (bytes32 codeHash); function getCodeHash(uint256 _input) external view returns (bytes32 codeHash); function getCodeSize(uint256 _input) external view returns (uint256 codeSize); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../libraries/TransactionHelper.sol"; interface IBootloaderUtilities { function getTransactionHashes( Transaction calldata _transaction ) external view returns (bytes32 txHash, bytes32 signedTxHash); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IBytecodeCompressor { function publishCompressedBytecode( bytes calldata _bytecode, bytes calldata _rawCompressedData ) external payable returns (bytes32 bytecodeHash); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IContractDeployer { /// @notice Defines the version of the account abstraction protocol /// that a contract claims to follow. /// - `None` means that the account is just a contract and it should never be interacted /// with as a custom account /// - `Version1` means that the account follows the first version of the account abstraction protocol enum AccountAbstractionVersion { None, Version1 } /// @notice Defines the nonce ordering used by the account /// - `Sequential` means that it is expected that the nonces are monotonic and increment by 1 /// at a time (the same as EOAs). /// - `Arbitrary` means that the nonces for the accounts can be arbitrary. The operator /// should serve the transactions from such an account on a first-come-first-serve basis. /// @dev This ordering is more of a suggestion to the operator on how the AA expects its transactions /// to be processed and is not considered as a system invariant. enum AccountNonceOrdering { Sequential, Arbitrary } struct AccountInfo { AccountAbstractionVersion supportedAAVersion; AccountNonceOrdering nonceOrdering; } event ContractDeployed( address indexed deployerAddress, bytes32 indexed bytecodeHash, address indexed contractAddress ); event AccountNonceOrderingUpdated(address indexed accountAddress, AccountNonceOrdering nonceOrdering); event AccountVersionUpdated(address indexed accountAddress, AccountAbstractionVersion aaVersion); function getNewAddressCreate2( address _sender, bytes32 _bytecodeHash, bytes32 _salt, bytes calldata _input ) external view returns (address newAddress); function getNewAddressCreate(address _sender, uint256 _senderNonce) external pure returns (address newAddress); function create2( bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input ) external payable returns (address newAddress); function create2Account( bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input, AccountAbstractionVersion _aaVersion ) external payable returns (address newAddress); /// @dev While the `_salt` parameter is not used anywhere here, /// it is still needed for consistency between `create` and /// `create2` functions (required by the compiler). function create( bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input ) external payable returns (address newAddress); /// @dev While `_salt` is never used here, we leave it here as a parameter /// for the consistency with the `create` function. function createAccount( bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input, AccountAbstractionVersion _aaVersion ) external payable returns (address newAddress); /// @notice Returns the information about a certain AA. function getAccountInfo(address _address) external view returns (AccountInfo memory info); /// @notice Can be called by an account to update its account version function updateAccountVersion(AccountAbstractionVersion _version) external; /// @notice Can be called by an account to update its nonce ordering function updateNonceOrdering(AccountNonceOrdering _nonceOrdering) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IEthToken { function balanceOf(uint256) external view returns (uint256); function transferFromTo(address _from, address _to, uint256 _amount) external; function totalSupply() external view returns (uint256); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function mint(address _account, uint256 _amount) external; function withdraw(address _l1Receiver) external payable; event Mint(address indexed account, uint256 amount); event Transfer(address indexed from, address indexed to, uint256 value); event Withdrawal(address indexed _l2Sender, address indexed _l1Receiver, uint256 _amount); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; struct ImmutableData { uint256 index; bytes32 value; } interface IImmutableSimulator { function getImmutable(address _dest, uint256 _index) external view returns (bytes32); function setImmutables(address _dest, ImmutableData[] calldata _immutables) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IKnownCodesStorage { event MarkedAsKnown(bytes32 indexed bytecodeHash, bool indexed sendBytecodeToL1); function markFactoryDeps(bool _shouldSendToL1, bytes32[] calldata _hashes) external; function markBytecodeAsPublished( bytes32 _bytecodeHash, bytes32 _l1PreimageHash, uint256 _l1PreimageBytesLen ) external; function getMarker(bytes32 _hash) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IL1Messenger { // Possibly in the future we will be able to track the messages sent to L1 with // some hooks in the VM. For now, it is much easier to track them with L2 events. event L1MessageSent(address indexed _sender, bytes32 indexed _hash, bytes _message); function sendToL1(bytes memory _message) external returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @author Matter Labs * @dev Interface of the nonce holder contract -- a contract used by the system to ensure * that there is always a unique identifier for a transaction with a particular account (we call it nonce). * In other words, the pair of (address, nonce) should always be unique. * @dev Custom accounts should use methods of this contract to store nonces or other possible unique identifiers * for the transaction. */ interface INonceHolder { event ValueSetUnderNonce(address indexed accountAddress, uint256 indexed key, uint256 value); /// @dev Returns the current minimal nonce for account. function getMinNonce(address _address) external view returns (uint256); /// @dev Returns the raw version of the current minimal nonce /// (equal to minNonce + 2^128 * deployment nonce). function getRawNonce(address _address) external view returns (uint256); /// @dev Increases the minimal nonce for the msg.sender. function increaseMinNonce(uint256 _value) external returns (uint256); /// @dev Sets the nonce value `key` as used. function setValueUnderNonce(uint256 _key, uint256 _value) external; /// @dev Gets the value stored inside a custom nonce. function getValueUnderNonce(uint256 _key) external view returns (uint256); /// @dev A convenience method to increment the minimal nonce if it is equal /// to the `_expectedNonce`. function incrementMinNonceIfEquals(uint256 _expectedNonce) external; /// @dev Returns the deployment nonce for the accounts used for CREATE opcode. function getDeploymentNonce(address _address) external view returns (uint256); /// @dev Increments the deployment nonce for the account and returns the previous one. function incrementDeploymentNonce(address _address) external returns (uint256); /// @dev Determines whether a certain nonce has been already used for an account. function validateNonceUsage(address _address, uint256 _key, bool _shouldBeUsed) external view; /// @dev Returns whether a nonce has been used for an account. function isNonceUsed(address _address, uint256 _nonce) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../libraries/TransactionHelper.sol"; enum ExecutionResult { Revert, Success } bytes4 constant PAYMASTER_VALIDATION_SUCCESS_MAGIC = IPaymaster.validateAndPayForPaymasterTransaction.selector; interface IPaymaster { /// @dev Called by the bootloader to verify that the paymaster agrees to pay for the /// fee for the transaction. This transaction should also send the necessary amount of funds onto the bootloader /// address. /// @param _txHash The hash of the transaction /// @param _suggestedSignedHash The hash of the transaction that is signed by an EOA /// @param _transaction The transaction itself. /// @return magic The value that should be equal to the signature of the validateAndPayForPaymasterTransaction /// if the paymaster agrees to pay for the transaction. /// @return context The "context" of the transaction: an array of bytes of length at most 1024 bytes, which will be /// passed to the `postTransaction` method of the account. /// @dev The developer should strive to preserve as many steps as possible both for valid /// and invalid transactions as this very method is also used during the gas fee estimation /// (without some of the necessary data, e.g. signature). function validateAndPayForPaymasterTransaction( bytes32 _txHash, bytes32 _suggestedSignedHash, Transaction calldata _transaction ) external payable returns (bytes4 magic, bytes memory context); /// @dev Called by the bootloader after the execution of the transaction. Please note that /// there is no guarantee that this method will be called at all. Unlike the original EIP4337, /// this method won't be called if the transaction execution results in out-of-gas. /// @param _context, the context of the execution, returned by the "validateAndPayForPaymasterTransaction" method. /// @param _transaction, the users' transaction. /// @param _txResult, the result of the transaction execution (success or failure). /// @param _maxRefundedGas, the upper bound on the amout of gas that could be refunded to the paymaster. /// @dev The exact amount refunded depends on the gas spent by the "postOp" itself and so the developers should /// take that into account. function postTransaction( bytes calldata _context, Transaction calldata _transaction, bytes32 _txHash, bytes32 _suggestedSignedHash, ExecutionResult _txResult, uint256 _maxRefundedGas ) external payable; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @author Matter Labs * @dev The interface that is used for encoding/decoding of * different types of paymaster flows. * @notice This is NOT an interface to be implementated * by contracts. It is just used for encoding. */ interface IPaymasterFlow { function general(bytes calldata input) external; function approvalBased(address _token, uint256 _minAllowance, bytes calldata _innerInput) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @author Matter Labs * @notice Contract that stores some of the context variables, that may be either * block-scoped, tx-scoped or system-wide. */ interface ISystemContext { function chainId() external view returns (uint256); function origin() external view returns (address); function gasPrice() external view returns (uint256); function blockGasLimit() external view returns (uint256); function coinbase() external view returns (address); function difficulty() external view returns (uint256); function baseFee() external view returns (uint256); function blockHash(uint256 _block) external view returns (bytes32); function getBlockHashEVM(uint256 _block) external view returns (bytes32); function getBlockNumberAndTimestamp() external view returns (uint256 blockNumber, uint256 blockTimestamp); // Note, that for now, the implementation of the bootloader allows this variables to // be incremented multiple times inside a block, so it should not relied upon right now. function getBlockNumber() external view returns (uint256); function getBlockTimestamp() external view returns (uint256); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "./SystemContractHelper.sol"; import "./Utils.sol"; import {SHA256_SYSTEM_CONTRACT, KECCAK256_SYSTEM_CONTRACT} from "../Constants.sol"; /** * @author Matter Labs * @notice This library is used to perform ultra-efficient calls using zkEVM-specific features. * @dev EVM calls always accept a memory slice as input and return a memory slice as output. * Therefore, even if the user has a ready-made calldata slice, they still need to copy it to memory * before calling. This is especially inefficient for large inputs (proxies, multi-calls, etc.). * In turn, zkEVM operates over a fat pointer, which is a set of (memory page, offset, start, length) in the memory/calldata/returndata. * This allows forwarding the calldata slice as is, without copying it to memory. * @dev Fat pointer is not just an integer, it is an extended data type supported on the VM level. * zkEVM creates the wellformed fat pointers for all the calldata/returndata regions, later * the contract may manipulate the already created fat pointers to forward a slice of the data, but not * to create new fat pointers! * @dev The allowed operation on fat pointers are: * 1. `ptr.add` - Transforms `ptr.offset` into `ptr.offset + u32(_value)`. If overflow happens then it panics. * 2. `ptr.sub` - Transforms `ptr.offset` into `ptr.offset - u32(_value)`. If underflow happens then it panics. * 3. `ptr.pack` - Do the concatenation between the lowest 128 bits of the pointer itself and the highest 128 bits of `_value`. It is typically used to prepare the ABI for external calls. * 4. `ptr.shrink` - Transforms `ptr.length` into `ptr.length - u32(_shrink)`. If underflow happens then it panics. * @dev The call opcodes accept the fat pointer and change it to its canonical form before passing it to the child call * 1. `ptr.start` is transformed into `ptr.offset + ptr.start` * 2. `ptr.length` is transformed into `ptr.length - ptr.offset` * 3. `ptr.offset` is transformed into `0` */ library EfficientCall { /// @notice Call the `keccak256` without copying calldata to memory. /// @param _data The preimage data. /// @return The `keccak256` hash. function keccak(bytes calldata _data) internal view returns (bytes32) { bytes memory returnData = staticCall(gasleft(), KECCAK256_SYSTEM_CONTRACT, _data); require(returnData.length == 32, "keccak256 returned invalid data"); return bytes32(returnData); } /// @notice Call the `sha256` precompile without copying calldata to memory. /// @param _data The preimage data. /// @return The `sha256` hash. function sha(bytes calldata _data) internal view returns (bytes32) { bytes memory returnData = staticCall(gasleft(), SHA256_SYSTEM_CONTRACT, _data); require(returnData.length == 32, "sha returned invalid data"); return bytes32(returnData); } /// @notice Perform a `call` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _value The `msg.value` to send. /// @param _data The calldata to use for the call. /// @param _isSystem Whether the call should contain the `isSystem` flag. /// @return returnData The copied to memory return data. function call( uint256 _gas, address _address, uint256 _value, bytes calldata _data, bool _isSystem ) internal returns (bytes memory returnData) { bool success = rawCall(_gas, _address, _value, _data, _isSystem); returnData = _verifyCallResult(success); } /// @notice Perform a `staticCall` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @return returnData The copied to memory return data. function staticCall( uint256 _gas, address _address, bytes calldata _data ) internal view returns (bytes memory returnData) { bool success = rawStaticCall(_gas, _address, _data); returnData = _verifyCallResult(success); } /// @notice Perform a `delegateCall` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @return returnData The copied to memory return data. function delegateCall( uint256 _gas, address _address, bytes calldata _data ) internal returns (bytes memory returnData) { bool success = rawDelegateCall(_gas, _address, _data); returnData = _verifyCallResult(success); } /// @notice Perform a `mimicCall` (a call with custom msg.sender) without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @param _whoToMimic The `msg.sender` for the next call. /// @param _isConstructor Whether the call should contain the `isConstructor` flag. /// @param _isSystem Whether the call should contain the `isSystem` flag. /// @return returnData The copied to memory return data. function mimicCall( uint256 _gas, address _address, bytes calldata _data, address _whoToMimic, bool _isConstructor, bool _isSystem ) internal returns (bytes memory returnData) { bool success = rawMimicCall(_gas, _address, _data, _whoToMimic, _isConstructor, _isSystem); returnData = _verifyCallResult(success); } /// @notice Perform a `call` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _value The `msg.value` to send. /// @param _data The calldata to use for the call. /// @param _isSystem Whether the call should contain the `isSystem` flag. /// @return success whether the call was successful. function rawCall( uint256 _gas, address _address, uint256 _value, bytes calldata _data, bool _isSystem ) internal returns (bool success) { if (_value == 0) { _loadFarCallABIIntoActivePtr(_gas, _data, false, _isSystem); address callAddr = RAW_FAR_CALL_BY_REF_CALL_ADDRESS; assembly { success := call(_address, callAddr, 0, 0, 0xFFFF, 0, 0) } } else { _loadFarCallABIIntoActivePtr(_gas, _data, false, true); // If there is provided `msg.value` call the `MsgValueSimulator` to forward ether. address msgValueSimulator = MSG_VALUE_SYSTEM_CONTRACT; address callAddr = SYSTEM_CALL_BY_REF_CALL_ADDRESS; // We need to supply the mask to the MsgValueSimulator to denote // that the call should be a system one. uint256 forwardMask = _isSystem ? MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT : 0; assembly { success := call(msgValueSimulator, callAddr, _value, _address, 0xFFFF, forwardMask, 0) } } } /// @notice Perform a `staticCall` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @return success whether the call was successful. function rawStaticCall(uint256 _gas, address _address, bytes calldata _data) internal view returns (bool success) { _loadFarCallABIIntoActivePtr(_gas, _data, false, false); address callAddr = RAW_FAR_CALL_BY_REF_CALL_ADDRESS; assembly { success := staticcall(_address, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Perform a `delegatecall` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @return success whether the call was successful. function rawDelegateCall(uint256 _gas, address _address, bytes calldata _data) internal returns (bool success) { _loadFarCallABIIntoActivePtr(_gas, _data, false, false); address callAddr = RAW_FAR_CALL_BY_REF_CALL_ADDRESS; assembly { success := delegatecall(_address, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Perform a `mimicCall` (call with custom msg.sender) without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @param _whoToMimic The `msg.sender` for the next call. /// @param _isConstructor Whether the call should contain the `isConstructor` flag. /// @param _isSystem Whether the call should contain the `isSystem` flag. /// @return success whether the call was successful. /// @dev If called not in kernel mode, it will result in a revert (enforced by the VM) function rawMimicCall( uint256 _gas, address _address, bytes calldata _data, address _whoToMimic, bool _isConstructor, bool _isSystem ) internal returns (bool success) { _loadFarCallABIIntoActivePtr(_gas, _data, _isConstructor, _isSystem); address callAddr = MIMIC_CALL_BY_REF_CALL_ADDRESS; uint256 cleanupMask = ADDRESS_MASK; assembly { // Clearing values before usage in assembly, since Solidity // doesn't do it by default _whoToMimic := and(_whoToMimic, cleanupMask) success := call(_address, callAddr, 0, 0, _whoToMimic, 0, 0) } } /// @dev Verify that a low-level call was successful, and revert if it wasn't, by bubbling the revert reason. /// @param _success Whether the call was successful. /// @return returnData The copied to memory return data. function _verifyCallResult(bool _success) private pure returns (bytes memory returnData) { if (_success) { uint256 size; assembly { size := returndatasize() } returnData = new bytes(size); assembly { returndatacopy(add(returnData, 0x20), 0, size) } } else { propagateRevert(); } } /// @dev Propagate the revert reason from the current call to the caller. function propagateRevert() internal pure { assembly { let size := returndatasize() returndatacopy(0, 0, size) revert(0, size) } } /// @dev Load the far call ABI into active ptr, that will be used for the next call by reference. /// @param _gas The gas to be passed to the call. /// @param _data The calldata to be passed to the call. /// @param _isConstructor Whether the call is a constructor call. /// @param _isSystem Whether the call is a system call. function _loadFarCallABIIntoActivePtr( uint256 _gas, bytes calldata _data, bool _isConstructor, bool _isSystem ) private view { SystemContractHelper.loadCalldataIntoActivePtr(); // Currently, zkEVM considers the pointer valid if(ptr.offset < ptr.length || (ptr.length == 0 && ptr.offset == 0)), otherwise panics. // So, if the data is empty we need to make the `ptr.length = ptr.offset = 0`, otherwise follow standard logic. if (_data.length == 0) { // Safe to cast, offset is never bigger than `type(uint32).max` SystemContractHelper.ptrShrinkIntoActive(uint32(msg.data.length)); } else { uint256 dataOffset; assembly { dataOffset := _data.offset } // Safe to cast, offset is never bigger than `type(uint32).max` SystemContractHelper.ptrAddIntoActive(uint32(dataOffset)); // Safe to cast, `data.length` is never bigger than `type(uint32).max` uint32 shrinkTo = uint32(msg.data.length - (_data.length + dataOffset)); SystemContractHelper.ptrShrinkIntoActive(shrinkTo); } uint32 gas = Utils.safeCastToU32(_gas); uint256 farCallAbi = SystemContractsCaller.getFarCallABIWithEmptyFatPointer( gas, // Only rollup is supported for now 0, CalldataForwardingMode.ForwardFatPointer, _isConstructor, _isSystem ); SystemContractHelper.ptrPackIntoActivePtr(farCallAbi); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library RLPEncoder { function encodeAddress(address _val) internal pure returns (bytes memory encoded) { // The size is equal to 20 bytes of the address itself + 1 for encoding bytes length in RLP. encoded = new bytes(0x15); bytes20 shiftedVal = bytes20(_val); assembly { // In the first byte we write the encoded length as 0x80 + 0x14 == 0x94. mstore(add(encoded, 0x20), 0x9400000000000000000000000000000000000000000000000000000000000000) // Write address data without stripping zeros. mstore(add(encoded, 0x21), shiftedVal) } } function encodeUint256(uint256 _val) internal pure returns (bytes memory encoded) { unchecked { if (_val < 128) { encoded = new bytes(1); // Handle zero as a non-value, since stripping zeroes results in an empty byte array encoded[0] = (_val == 0) ? bytes1(uint8(128)) : bytes1(uint8(_val)); } else { uint256 hbs = _highestByteSet(_val); encoded = new bytes(hbs + 2); encoded[0] = bytes1(uint8(hbs + 0x81)); uint256 lbs = 31 - hbs; uint256 shiftedVal = _val << (lbs * 8); assembly { mstore(add(encoded, 0x21), shiftedVal) } } } } /// @notice Encodes the size of bytes in RLP format. /// @param _len The length of the bytes to encode. It has a `uint64` type since as larger values are not supported. /// NOTE: panics if the length is 1 since the length encoding is ambiguous in this case. function encodeNonSingleBytesLen(uint64 _len) internal pure returns (bytes memory) { assert(_len != 1); return _encodeLength(_len, 0x80); } /// @notice Encodes the size of list items in RLP format. /// @param _len The length of the bytes to encode. It has a `uint64` type since as larger values are not supported. function encodeListLen(uint64 _len) internal pure returns (bytes memory) { return _encodeLength(_len, 0xc0); } function _encodeLength(uint64 _len, uint256 _offset) private pure returns (bytes memory encoded) { unchecked { if (_len < 56) { encoded = new bytes(1); encoded[0] = bytes1(uint8(_len + _offset)); } else { uint256 hbs = _highestByteSet(uint256(_len)); encoded = new bytes(hbs + 2); encoded[0] = bytes1(uint8(_offset + hbs + 56)); uint256 lbs = 31 - hbs; uint256 shiftedVal = uint256(_len) << (lbs * 8); assembly { mstore(add(encoded, 0x21), shiftedVal) } } } } /// @notice Computes the index of the highest byte set in number. /// @notice Uses little endian ordering (The least significant byte has index `0`). /// NOTE: returns `0` for `0` function _highestByteSet(uint256 _number) private pure returns (uint256 hbs) { unchecked { if (_number > type(uint128).max) { _number >>= 128; hbs += 16; } if (_number > type(uint64).max) { _number >>= 64; hbs += 8; } if (_number > type(uint32).max) { _number >>= 32; hbs += 4; } if (_number > type(uint16).max) { _number >>= 16; hbs += 2; } if (_number > type(uint8).max) { hbs += 1; } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8; import {MAX_SYSTEM_CONTRACT_ADDRESS, MSG_VALUE_SYSTEM_CONTRACT} from "../Constants.sol"; import "./SystemContractsCaller.sol"; import "./Utils.sol"; uint256 constant UINT32_MASK = 0xffffffff; uint256 constant UINT128_MASK = 0xffffffffffffffffffffffffffffffff; /// @dev The mask that is used to convert any uint256 to a proper address. /// It needs to be padded with `00` to be treated as uint256 by Solidity uint256 constant ADDRESS_MASK = 0x00ffffffffffffffffffffffffffffffffffffffff; struct ZkSyncMeta { uint32 gasPerPubdataByte; uint32 heapSize; uint32 auxHeapSize; uint8 shardId; uint8 callerShardId; uint8 codeShardId; } enum Global { CalldataPtr, CallFlags, ExtraABIData1, ExtraABIData2, ReturndataPtr } /** * @author Matter Labs * @notice Library used for accessing zkEVM-specific opcodes, needed for the development * of system contracts. * @dev While this library will be eventually available to public, some of the provided * methods won't work for non-system contracts. We will not recommend this library * for external use. */ library SystemContractHelper { /// @notice Send an L2Log to L1. /// @param _isService The `isService` flag. /// @param _key The `key` part of the L2Log. /// @param _value The `value` part of the L2Log. /// @dev The meaning of all these parameters is context-dependent, but they /// have no intrinsic meaning per se. function toL1(bool _isService, bytes32 _key, bytes32 _value) internal { address callAddr = TO_L1_CALL_ADDRESS; assembly { // Ensuring that the type is bool _isService := and(_isService, 1) // This `success` is always 0, but the method always succeeds // (except for the cases when there is not enough gas) let success := call(_isService, callAddr, _key, _value, 0xFFFF, 0, 0) } } /// @notice Get address of the currently executed code. /// @dev This allows differentiating between `call` and `delegatecall`. /// During the former `this` and `codeAddress` are the same, while /// during the latter they are not. function getCodeAddress() internal view returns (address addr) { address callAddr = CODE_ADDRESS_CALL_ADDRESS; assembly { addr := staticcall(0, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Provide a compiler hint, by placing calldata fat pointer into virtual `ACTIVE_PTR`, /// that can be manipulated by `ptr.add`/`ptr.sub`/`ptr.pack`/`ptr.shrink` later. /// @dev This allows making a call by forwarding calldata pointer to the child call. /// It is a much more efficient way to forward calldata, than standard EVM bytes copying. function loadCalldataIntoActivePtr() internal view { address callAddr = LOAD_CALLDATA_INTO_ACTIVE_PTR_CALL_ADDRESS; assembly { pop(staticcall(0, callAddr, 0, 0xFFFF, 0, 0)) } } /// @notice Compiler simulation of the `ptr.pack` opcode for the virtual `ACTIVE_PTR` pointer. /// @dev Do the concatenation between lowest part of `ACTIVE_PTR` and highest part of `_farCallAbi` /// forming packed fat pointer for a far call or ret ABI when necessary. /// Note: Panics if the lowest 128 bits of `_farCallAbi` are not zeroes. function ptrPackIntoActivePtr(uint256 _farCallAbi) internal view { address callAddr = PTR_PACK_INTO_ACTIVE_CALL_ADDRESS; assembly { pop(staticcall(_farCallAbi, callAddr, 0, 0xFFFF, 0, 0)) } } /// @notice Compiler simulation of the `ptr.add` opcode for the virtual `ACTIVE_PTR` pointer. /// @dev Transforms `ACTIVE_PTR.offset` into `ACTIVE_PTR.offset + u32(_value)`. If overflow happens then it panics. function ptrAddIntoActive(uint32 _value) internal view { address callAddr = PTR_ADD_INTO_ACTIVE_CALL_ADDRESS; uint256 cleanupMask = UINT32_MASK; assembly { // Clearing input params as they are not cleaned by Solidity by default _value := and(_value, cleanupMask) pop(staticcall(_value, callAddr, 0, 0xFFFF, 0, 0)) } } /// @notice Compiler simulation of the `ptr.shrink` opcode for the virtual `ACTIVE_PTR` pointer. /// @dev Transforms `ACTIVE_PTR.length` into `ACTIVE_PTR.length - u32(_shrink)`. If underflow happens then it panics. function ptrShrinkIntoActive(uint32 _shrink) internal view { address callAddr = PTR_SHRINK_INTO_ACTIVE_CALL_ADDRESS; uint256 cleanupMask = UINT32_MASK; assembly { // Clearing input params as they are not cleaned by Solidity by default _shrink := and(_shrink, cleanupMask) pop(staticcall(_shrink, callAddr, 0, 0xFFFF, 0, 0)) } } /// @notice packs precompile parameters into one word /// @param _inputMemoryOffset The memory offset in 32-byte words for the input data for calling the precompile. /// @param _inputMemoryLength The length of the input data in words. /// @param _outputMemoryOffset The memory offset in 32-byte words for the output data. /// @param _outputMemoryLength The length of the output data in words. /// @param _perPrecompileInterpreted The constant, the meaning of which is defined separately for /// each precompile. For information, please read the documentation of the precompilecall log in /// the VM. function packPrecompileParams( uint32 _inputMemoryOffset, uint32 _inputMemoryLength, uint32 _outputMemoryOffset, uint32 _outputMemoryLength, uint64 _perPrecompileInterpreted ) internal pure returns (uint256 rawParams) { rawParams = _inputMemoryOffset; rawParams |= uint256(_inputMemoryLength) << 32; rawParams |= uint256(_outputMemoryOffset) << 64; rawParams |= uint256(_outputMemoryLength) << 96; rawParams |= uint256(_perPrecompileInterpreted) << 192; } /// @notice Call precompile with given parameters. /// @param _rawParams The packed precompile params. They can be retrieved by /// the `packPrecompileParams` method. /// @param _gasToBurn The number of gas to burn during this call. /// @return success Whether the call was successful. /// @dev The list of currently available precompiles sha256, keccak256, ecrecover. /// NOTE: The precompile type depends on `this` which calls precompile, which means that only /// system contracts corresponding to the list of precompiles above can do `precompileCall`. /// @dev If used not in the `sha256`, `keccak256` or `ecrecover` contracts, it will just burn the gas provided. function precompileCall(uint256 _rawParams, uint32 _gasToBurn) internal view returns (bool success) { address callAddr = PRECOMPILE_CALL_ADDRESS; // After `precompileCall` gas will be burned down to 0 if there are not enough of them, // thats why it should be checked before the call. require(gasleft() >= _gasToBurn); uint256 cleanupMask = UINT32_MASK; assembly { // Clearing input params as they are not cleaned by Solidity by default _gasToBurn := and(_gasToBurn, cleanupMask) success := staticcall(_rawParams, callAddr, _gasToBurn, 0xFFFF, 0, 0) } } /// @notice Set `msg.value` to next far call. /// @param _value The msg.value that will be used for the *next* call. /// @dev If called not in kernel mode, it will result in a revert (enforced by the VM) function setValueForNextFarCall(uint128 _value) internal returns (bool success) { uint256 cleanupMask = UINT128_MASK; address callAddr = SET_CONTEXT_VALUE_CALL_ADDRESS; assembly { // Clearing input params as they are not cleaned by Solidity by default _value := and(_value, cleanupMask) success := call(0, callAddr, _value, 0, 0xFFFF, 0, 0) } } /// @notice Initialize a new event. /// @param initializer The event initializing value. /// @param value1 The first topic or data chunk. function eventInitialize(uint256 initializer, uint256 value1) internal { address callAddr = EVENT_INITIALIZE_ADDRESS; assembly { pop(call(initializer, callAddr, value1, 0, 0xFFFF, 0, 0)) } } /// @notice Continue writing the previously initialized event. /// @param value1 The first topic or data chunk. /// @param value2 The second topic or data chunk. function eventWrite(uint256 value1, uint256 value2) internal { address callAddr = EVENT_WRITE_ADDRESS; assembly { pop(call(value1, callAddr, value2, 0, 0xFFFF, 0, 0)) } } /// @notice Get the packed representation of the `ZkSyncMeta` from the current context. /// @return meta The packed representation of the ZkSyncMeta. /// @dev The fields in ZkSyncMeta are NOT tightly packed, i.e. there is a special rule on how /// they are packed. For more information, please read the documentation on ZkSyncMeta. function getZkSyncMetaBytes() internal view returns (uint256 meta) { address callAddr = META_CALL_ADDRESS; assembly { meta := staticcall(0, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Returns the bits [offset..offset+size-1] of the meta. /// @param meta Packed representation of the ZkSyncMeta. /// @param offset The offset of the bits. /// @param size The size of the extracted number in bits. /// @return result The extracted number. function extractNumberFromMeta(uint256 meta, uint256 offset, uint256 size) internal pure returns (uint256 result) { // Firstly, we delete all the bits after the field uint256 shifted = (meta << (256 - size - offset)); // Then we shift everything back result = (shifted >> (256 - size)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the number of gas /// that a single byte sent to L1 as pubdata costs. /// @param meta Packed representation of the ZkSyncMeta. /// @return gasPerPubdataByte The current price in gas per pubdata byte. function getGasPerPubdataByteFromMeta(uint256 meta) internal pure returns (uint32 gasPerPubdataByte) { gasPerPubdataByte = uint32(extractNumberFromMeta(meta, META_GAS_PER_PUBDATA_BYTE_OFFSET, 32)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the number of the current size /// of the heap in bytes. /// @param meta Packed representation of the ZkSyncMeta. /// @return heapSize The size of the memory in bytes byte. /// @dev The following expression: getHeapSizeFromMeta(getZkSyncMetaBytes()) is /// equivalent to the MSIZE in Solidity. function getHeapSizeFromMeta(uint256 meta) internal pure returns (uint32 heapSize) { heapSize = uint32(extractNumberFromMeta(meta, META_HEAP_SIZE_OFFSET, 32)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the number of the current size /// of the auxilary heap in bytes. /// @param meta Packed representation of the ZkSyncMeta. /// @return auxHeapSize The size of the auxilary memory in bytes byte. /// @dev You can read more on auxilary memory in the VM1.2 documentation. function getAuxHeapSizeFromMeta(uint256 meta) internal pure returns (uint32 auxHeapSize) { auxHeapSize = uint32(extractNumberFromMeta(meta, META_AUX_HEAP_SIZE_OFFSET, 32)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the shardId of `this`. /// @param meta Packed representation of the ZkSyncMeta. /// @return shardId The shardId of `this`. /// @dev Currently only shard 0 (zkRollup) is supported. function getShardIdFromMeta(uint256 meta) internal pure returns (uint8 shardId) { shardId = uint8(extractNumberFromMeta(meta, META_SHARD_ID_OFFSET, 8)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the shardId of /// the msg.sender. /// @param meta Packed representation of the ZkSyncMeta. /// @return callerShardId The shardId of the msg.sender. /// @dev Currently only shard 0 (zkRollup) is supported. function getCallerShardIdFromMeta(uint256 meta) internal pure returns (uint8 callerShardId) { callerShardId = uint8(extractNumberFromMeta(meta, META_CALLER_SHARD_ID_OFFSET, 8)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the shardId of /// the currently executed code. /// @param meta Packed representation of the ZkSyncMeta. /// @return codeShardId The shardId of the currently executed code. /// @dev Currently only shard 0 (zkRollup) is supported. function getCodeShardIdFromMeta(uint256 meta) internal pure returns (uint8 codeShardId) { codeShardId = uint8(extractNumberFromMeta(meta, META_CODE_SHARD_ID_OFFSET, 8)); } /// @notice Retrieves the ZkSyncMeta structure. /// @return meta The ZkSyncMeta execution context parameters. function getZkSyncMeta() internal view returns (ZkSyncMeta memory meta) { uint256 metaPacked = getZkSyncMetaBytes(); meta.gasPerPubdataByte = getGasPerPubdataByteFromMeta(metaPacked); meta.shardId = getShardIdFromMeta(metaPacked); meta.callerShardId = getCallerShardIdFromMeta(metaPacked); meta.codeShardId = getCodeShardIdFromMeta(metaPacked); } /// @notice Returns the call flags for the current call. /// @return callFlags The bitmask of the callflags. /// @dev Call flags is the value of the first register /// at the start of the call. /// @dev The zero bit of the callFlags indicates whether the call is /// a constructor call. The first bit of the callFlags indicates whether /// the call is a system one. function getCallFlags() internal view returns (uint256 callFlags) { address callAddr = CALLFLAGS_CALL_ADDRESS; assembly { callFlags := staticcall(0, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Returns the current calldata pointer. /// @return ptr The current calldata pointer. /// @dev NOTE: This file is just an integer and it can not be used /// to forward the calldata to the next calls in any way. function getCalldataPtr() internal view returns (uint256 ptr) { address callAddr = PTR_CALLDATA_CALL_ADDRESS; assembly { ptr := staticcall(0, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Returns the N-th extraAbiParam for the current call. /// @return extraAbiData The value of the N-th extraAbiParam for this call. /// @dev It is equal to the value of the (N+2)-th register /// at the start of the call. function getExtraAbiData(uint256 index) internal view returns (uint256 extraAbiData) { require(index < 10, "There are only 10 accessible registers"); address callAddr = GET_EXTRA_ABI_DATA_ADDRESS; assembly { extraAbiData := staticcall(index, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Retuns whether the current call is a system call. /// @return `true` or `false` based on whether the current call is a system call. function isSystemCall() internal view returns (bool) { uint256 callFlags = getCallFlags(); // When the system call is passed, the 2-bit it set to 1 return (callFlags & 2) != 0; } /// @notice Returns whether the address is a system contract. /// @param _address The address to test /// @return `true` or `false` based on whether the `_address` is a system contract. function isSystemContract(address _address) internal pure returns (bool) { return uint160(_address) <= uint160(MAX_SYSTEM_CONTRACT_ADDRESS); } } /// @dev Solidity does not allow exporting modifiers via libraries, so /// the only way to do reuse modifiers is to have a base contract abstract contract ISystemContract { /// @notice Modifier that makes sure that the method /// can only be called via a system call. modifier onlySystemCall() { require( SystemContractHelper.isSystemCall() || SystemContractHelper.isSystemContract(msg.sender), "This method require system call flag" ); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8; import {MSG_VALUE_SYSTEM_CONTRACT, MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT} from "../Constants.sol"; import "./Utils.sol"; // Addresses used for the compiler to be replaced with the // zkSync-specific opcodes during the compilation. // IMPORTANT: these are just compile-time constants and are used // only if used in-place by Yul optimizer. address constant TO_L1_CALL_ADDRESS = address((1 << 16) - 1); address constant CODE_ADDRESS_CALL_ADDRESS = address((1 << 16) - 2); address constant PRECOMPILE_CALL_ADDRESS = address((1 << 16) - 3); address constant META_CALL_ADDRESS = address((1 << 16) - 4); address constant MIMIC_CALL_CALL_ADDRESS = address((1 << 16) - 5); address constant SYSTEM_MIMIC_CALL_CALL_ADDRESS = address((1 << 16) - 6); address constant MIMIC_CALL_BY_REF_CALL_ADDRESS = address((1 << 16) - 7); address constant SYSTEM_MIMIC_CALL_BY_REF_CALL_ADDRESS = address((1 << 16) - 8); address constant RAW_FAR_CALL_CALL_ADDRESS = address((1 << 16) - 9); address constant RAW_FAR_CALL_BY_REF_CALL_ADDRESS = address((1 << 16) - 10); address constant SYSTEM_CALL_CALL_ADDRESS = address((1 << 16) - 11); address constant SYSTEM_CALL_BY_REF_CALL_ADDRESS = address((1 << 16) - 12); address constant SET_CONTEXT_VALUE_CALL_ADDRESS = address((1 << 16) - 13); address constant SET_PUBDATA_PRICE_CALL_ADDRESS = address((1 << 16) - 14); address constant INCREMENT_TX_COUNTER_CALL_ADDRESS = address((1 << 16) - 15); address constant PTR_CALLDATA_CALL_ADDRESS = address((1 << 16) - 16); address constant CALLFLAGS_CALL_ADDRESS = address((1 << 16) - 17); address constant PTR_RETURNDATA_CALL_ADDRESS = address((1 << 16) - 18); address constant EVENT_INITIALIZE_ADDRESS = address((1 << 16) - 19); address constant EVENT_WRITE_ADDRESS = address((1 << 16) - 20); address constant LOAD_CALLDATA_INTO_ACTIVE_PTR_CALL_ADDRESS = address((1 << 16) - 21); address constant LOAD_LATEST_RETURNDATA_INTO_ACTIVE_PTR_CALL_ADDRESS = address((1 << 16) - 22); address constant PTR_ADD_INTO_ACTIVE_CALL_ADDRESS = address((1 << 16) - 23); address constant PTR_SHRINK_INTO_ACTIVE_CALL_ADDRESS = address((1 << 16) - 24); address constant PTR_PACK_INTO_ACTIVE_CALL_ADDRESS = address((1 << 16) - 25); address constant MULTIPLICATION_HIGH_ADDRESS = address((1 << 16) - 26); address constant GET_EXTRA_ABI_DATA_ADDRESS = address((1 << 16) - 27); // All the offsets are in bits uint256 constant META_GAS_PER_PUBDATA_BYTE_OFFSET = 0 * 8; uint256 constant META_HEAP_SIZE_OFFSET = 8 * 8; uint256 constant META_AUX_HEAP_SIZE_OFFSET = 12 * 8; uint256 constant META_SHARD_ID_OFFSET = 28 * 8; uint256 constant META_CALLER_SHARD_ID_OFFSET = 29 * 8; uint256 constant META_CODE_SHARD_ID_OFFSET = 30 * 8; /// @notice The way to forward the calldata: /// - Use the current heap (i.e. the same as on EVM). /// - Use the auxiliary heap. /// - Forward via a pointer /// @dev Note, that currently, users do not have access to the auxiliary /// heap and so the only type of forwarding that will be used by the users /// are UseHeap and ForwardFatPointer for forwarding a slice of the current calldata /// to the next call. enum CalldataForwardingMode { UseHeap, ForwardFatPointer, UseAuxHeap } /** * @author Matter Labs * @notice A library that allows calling contracts with the `isSystem` flag. * @dev It is needed to call ContractDeployer and NonceHolder. */ library SystemContractsCaller { /// @notice Makes a call with the `isSystem` flag. /// @param gasLimit The gas limit for the call. /// @param to The address to call. /// @param value The value to pass with the transaction. /// @param data The calldata. /// @return success Whether the transaction has been successful. /// @dev Note, that the `isSystem` flag can only be set when calling system contracts. function systemCall(uint32 gasLimit, address to, uint256 value, bytes memory data) internal returns (bool success) { address callAddr = SYSTEM_CALL_CALL_ADDRESS; uint32 dataStart; assembly { dataStart := add(data, 0x20) } uint32 dataLength = uint32(Utils.safeCastToU32(data.length)); uint256 farCallAbi = SystemContractsCaller.getFarCallABI( 0, 0, dataStart, dataLength, gasLimit, // Only rollup is supported for now 0, CalldataForwardingMode.UseHeap, false, true ); if (value == 0) { // Doing the system call directly assembly { success := call(to, callAddr, 0, 0, farCallAbi, 0, 0) } } else { address msgValueSimulator = MSG_VALUE_SYSTEM_CONTRACT; // We need to supply the mask to the MsgValueSimulator to denote // that the call should be a system one. uint256 forwardMask = MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT; assembly { success := call(msgValueSimulator, callAddr, value, to, farCallAbi, forwardMask, 0) } } } /// @notice Makes a call with the `isSystem` flag. /// @param gasLimit The gas limit for the call. /// @param to The address to call. /// @param value The value to pass with the transaction. /// @param data The calldata. /// @return success Whether the transaction has been successful. /// @return returnData The returndata of the transaction (revert reason in case the transaction has failed). /// @dev Note, that the `isSystem` flag can only be set when calling system contracts. function systemCallWithReturndata( uint32 gasLimit, address to, uint128 value, bytes memory data ) internal returns (bool success, bytes memory returnData) { success = systemCall(gasLimit, to, value, data); uint256 size; assembly { size := returndatasize() } returnData = new bytes(size); assembly { returndatacopy(add(returnData, 0x20), 0, size) } } /// @notice Makes a call with the `isSystem` flag. /// @param gasLimit The gas limit for the call. /// @param to The address to call. /// @param value The value to pass with the transaction. /// @param data The calldata. /// @return returnData The returndata of the transaction. In case the transaction reverts, the error /// bubbles up to the parent frame. /// @dev Note, that the `isSystem` flag can only be set when calling system contracts. function systemCallWithPropagatedRevert( uint32 gasLimit, address to, uint128 value, bytes memory data ) internal returns (bytes memory returnData) { bool success; (success, returnData) = systemCallWithReturndata(gasLimit, to, value, data); if (!success) { assembly { let size := mload(returnData) revert(add(returnData, 0x20), size) } } } /// @notice Calculates the packed representation of the FarCallABI. /// @param dataOffset Calldata offset in memory. Provide 0 unless using custom pointer. /// @param memoryPage Memory page to use. Provide 0 unless using custom pointer. /// @param dataStart The start of the calldata slice. Provide the offset in memory /// if not using custom pointer. /// @param dataLength The calldata length. Provide the length of the calldata in bytes /// unless using custom pointer. /// @param gasPassed The gas to pass with the call. /// @param shardId Of the account to call. Currently only 0 is supported. /// @param forwardingMode The forwarding mode to use: /// - provide CalldataForwardingMode.UseHeap when using your current memory /// - provide CalldataForwardingMode.ForwardFatPointer when using custom pointer. /// @param isConstructorCall Whether the call will be a call to the constructor /// (ignored when the caller is not a system contract). /// @param isSystemCall Whether the call will have the `isSystem` flag. /// @return farCallAbi The far call ABI. /// @dev The `FarCallABI` has the following structure: /// pub struct FarCallABI { /// pub memory_quasi_fat_pointer: FatPointer, /// pub gas_passed: u32, /// pub shard_id: u8, /// pub forwarding_mode: FarCallForwardPageType, /// pub constructor_call: bool, /// pub to_system: bool, /// } /// /// The FatPointer struct: /// /// pub struct FatPointer { /// pub offset: u32, // offset relative to `start` /// pub memory_page: u32, // memory page where slice is located /// pub start: u32, // absolute start of the slice /// pub length: u32, // length of the slice /// } /// /// @dev Note, that the actual layout is the following: /// /// [0..32) bits -- the calldata offset /// [32..64) bits -- the memory page to use. Can be left blank in most of the cases. /// [64..96) bits -- the absolute start of the slice /// [96..128) bits -- the length of the slice. /// [128..192) bits -- empty bits. /// [192..224) bits -- gasPassed. /// [224..232) bits -- forwarding_mode /// [232..240) bits -- shard id. /// [240..248) bits -- constructor call flag /// [248..256] bits -- system call flag function getFarCallABI( uint32 dataOffset, uint32 memoryPage, uint32 dataStart, uint32 dataLength, uint32 gasPassed, uint8 shardId, CalldataForwardingMode forwardingMode, bool isConstructorCall, bool isSystemCall ) internal pure returns (uint256 farCallAbi) { // Fill in the call parameter fields farCallAbi = getFarCallABIWithEmptyFatPointer( gasPassed, shardId, forwardingMode, isConstructorCall, isSystemCall ); // Fill in the fat pointer fields farCallAbi |= dataOffset; farCallAbi |= (uint256(memoryPage) << 32); farCallAbi |= (uint256(dataStart) << 64); farCallAbi |= (uint256(dataLength) << 96); } /// @notice Calculates the packed representation of the FarCallABI with zero fat pointer fields. /// @param gasPassed The gas to pass with the call. /// @param shardId Of the account to call. Currently only 0 is supported. /// @param forwardingMode The forwarding mode to use: /// - provide CalldataForwardingMode.UseHeap when using your current memory /// - provide CalldataForwardingMode.ForwardFatPointer when using custom pointer. /// @param isConstructorCall Whether the call will be a call to the constructor /// (ignored when the caller is not a system contract). /// @param isSystemCall Whether the call will have the `isSystem` flag. /// @return farCallAbiWithEmptyFatPtr The far call ABI with zero fat pointer fields. function getFarCallABIWithEmptyFatPointer( uint32 gasPassed, uint8 shardId, CalldataForwardingMode forwardingMode, bool isConstructorCall, bool isSystemCall ) internal pure returns (uint256 farCallAbiWithEmptyFatPtr) { farCallAbiWithEmptyFatPtr |= (uint256(gasPassed) << 192); farCallAbiWithEmptyFatPtr |= (uint256(forwardingMode) << 224); farCallAbiWithEmptyFatPtr |= (uint256(shardId) << 232); if (isConstructorCall) { farCallAbiWithEmptyFatPtr |= (1 << 240); } if (isSystemCall) { farCallAbiWithEmptyFatPtr |= (1 << 248); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../openzeppelin/token/ERC20/IERC20.sol"; import "../openzeppelin/token/ERC20/utils/SafeERC20.sol"; import "../interfaces/IPaymasterFlow.sol"; import "../interfaces/IContractDeployer.sol"; import {ETH_TOKEN_SYSTEM_CONTRACT, BOOTLOADER_FORMAL_ADDRESS} from "../Constants.sol"; import "./RLPEncoder.sol"; import "./EfficientCall.sol"; /// @dev The type id of zkSync's EIP-712-signed transaction. uint8 constant EIP_712_TX_TYPE = 0x71; /// @dev The type id of legacy transactions. uint8 constant LEGACY_TX_TYPE = 0x0; /// @dev The type id of legacy transactions. uint8 constant EIP_2930_TX_TYPE = 0x01; /// @dev The type id of EIP1559 transactions. uint8 constant EIP_1559_TX_TYPE = 0x02; /// @notice Structure used to represent zkSync transaction. struct Transaction { // The type of the transaction. uint256 txType; // The caller. uint256 from; // The callee. uint256 to; // The gasLimit to pass with the transaction. // It has the same meaning as Ethereum's gasLimit. uint256 gasLimit; // The maximum amount of gas the user is willing to pay for a byte of pubdata. uint256 gasPerPubdataByteLimit; // The maximum fee per gas that the user is willing to pay. // It is akin to EIP1559's maxFeePerGas. uint256 maxFeePerGas; // The maximum priority fee per gas that the user is willing to pay. // It is akin to EIP1559's maxPriorityFeePerGas. uint256 maxPriorityFeePerGas; // The transaction's paymaster. If there is no paymaster, it is equal to 0. uint256 paymaster; // The nonce of the transaction. uint256 nonce; // The value to pass with the transaction. uint256 value; // In the future, we might want to add some // new fields to the struct. The `txData` struct // is to be passed to account and any changes to its structure // would mean a breaking change to these accounts. In order to prevent this, // we should keep some fields as "reserved". // It is also recommended that their length is fixed, since // it would allow easier proof integration (in case we will need // some special circuit for preprocessing transactions). uint256[4] reserved; // The transaction's calldata. bytes data; // The signature of the transaction. bytes signature; // The properly formatted hashes of bytecodes that must be published on L1 // with the inclusion of this transaction. Note, that a bytecode has been published // before, the user won't pay fees for its republishing. bytes32[] factoryDeps; // The input to the paymaster. bytes paymasterInput; // Reserved dynamic type for the future use-case. Using it should be avoided, // But it is still here, just in case we want to enable some additional functionality. bytes reservedDynamic; } /** * @author Matter Labs * @notice Library is used to help custom accounts to work with common methods for the Transaction type. */ library TransactionHelper { using SafeERC20 for IERC20; /// @notice The EIP-712 typehash for the contract's domain bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId)"); bytes32 constant EIP712_TRANSACTION_TYPE_HASH = keccak256( "Transaction(uint256 txType,uint256 from,uint256 to,uint256 gasLimit,uint256 gasPerPubdataByteLimit,uint256 maxFeePerGas,uint256 maxPriorityFeePerGas,uint256 paymaster,uint256 nonce,uint256 value,bytes data,bytes32[] factoryDeps,bytes paymasterInput)" ); /// @notice Whether the token is Ethereum. /// @param _addr The address of the token /// @return `true` or `false` based on whether the token is Ether. /// @dev This method assumes that address is Ether either if the address is 0 (for convenience) /// or if the address is the address of the L2EthToken system contract. function isEthToken(uint256 _addr) internal pure returns (bool) { return _addr == uint256(uint160(address(ETH_TOKEN_SYSTEM_CONTRACT))) || _addr == 0; } /// @notice Calculate the suggested signed hash of the transaction, /// i.e. the hash that is signed by EOAs and is recommended to be signed by other accounts. function encodeHash(Transaction calldata _transaction) internal view returns (bytes32 resultHash) { if (_transaction.txType == LEGACY_TX_TYPE) { resultHash = _encodeHashLegacyTransaction(_transaction); } else if (_transaction.txType == EIP_712_TX_TYPE) { resultHash = _encodeHashEIP712Transaction(_transaction); } else if (_transaction.txType == EIP_1559_TX_TYPE) { resultHash = _encodeHashEIP1559Transaction(_transaction); } else if (_transaction.txType == EIP_2930_TX_TYPE) { resultHash = _encodeHashEIP2930Transaction(_transaction); } else { // Currently no other transaction types are supported. // Any new transaction types will be processed in a similar manner. revert("Encoding unsupported tx"); } } /// @notice Encode hash of the zkSync native transaction type. /// @return keccak256 hash of the EIP-712 encoded representation of transaction function _encodeHashEIP712Transaction(Transaction calldata _transaction) private view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( EIP712_TRANSACTION_TYPE_HASH, _transaction.txType, _transaction.from, _transaction.to, _transaction.gasLimit, _transaction.gasPerPubdataByteLimit, _transaction.maxFeePerGas, _transaction.maxPriorityFeePerGas, _transaction.paymaster, _transaction.nonce, _transaction.value, EfficientCall.keccak(_transaction.data), keccak256(abi.encodePacked(_transaction.factoryDeps)), EfficientCall.keccak(_transaction.paymasterInput) ) ); bytes32 domainSeparator = keccak256( abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid) ); return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } /// @notice Encode hash of the legacy transaction type. /// @return keccak256 of the serialized RLP encoded representation of transaction function _encodeHashLegacyTransaction(Transaction calldata _transaction) private view returns (bytes32) { // Hash of legacy transactions are encoded as one of the: // - RLP(nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0) // - RLP(nonce, gasPrice, gasLimit, to, value, data) // // In this RLP encoding, only the first one above list appears, so we encode each element // inside list and then concatenate the length of all elements with them. bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); // Encode `gasPrice` and `gasLimit` together to prevent "stack too deep error". bytes memory encodedGasParam; { bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); encodedGasParam = bytes.concat(encodedGasPrice, encodedGasLimit); } bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // Encode `chainId` according to EIP-155, but only if the `chainId` is specified in the transaction. bytes memory encodedChainId; if (_transaction.reserved[0] != 0) { encodedChainId = bytes.concat(RLPEncoder.encodeUint256(block.chainid), hex"80_80"); } bytes memory encodedListLength; unchecked { uint256 listLength = encodedNonce.length + encodedGasParam.length + encodedTo.length + encodedValue.length + encodedDataLength.length + _transaction.data.length + encodedChainId.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( encodedListLength, encodedNonce, encodedGasParam, encodedTo, encodedValue, encodedDataLength, _transaction.data, encodedChainId ) ); } /// @notice Encode hash of the EIP2930 transaction type. /// @return keccak256 of the serialized RLP encoded representation of transaction function _encodeHashEIP2930Transaction(Transaction calldata _transaction) private view returns (bytes32) { // Hash of EIP2930 transactions is encoded the following way: // H(0x01 || RLP(chain_id, nonce, gas_price, gas_limit, destination, amount, data, access_list)) // // Note, that on zkSync access lists are not supported and should always be empty. // Encode all fixed-length params to avoid "stack too deep error" bytes memory encodedFixedLengthParams; { bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); encodedFixedLengthParams = bytes.concat( encodedChainId, encodedNonce, encodedGasPrice, encodedGasLimit, encodedTo, encodedValue ); } // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // On zkSync, access lists are always zero length (at least for now). bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); bytes memory encodedListLength; unchecked { uint256 listLength = encodedFixedLengthParams.length + encodedDataLength.length + _transaction.data.length + encodedAccessListLength.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( "\x01", encodedListLength, encodedFixedLengthParams, encodedDataLength, _transaction.data, encodedAccessListLength ) ); } /// @notice Encode hash of the EIP1559 transaction type. /// @return keccak256 of the serialized RLP encoded representation of transaction function _encodeHashEIP1559Transaction(Transaction calldata _transaction) private view returns (bytes32) { // Hash of EIP1559 transactions is encoded the following way: // H(0x02 || RLP(chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list)) // // Note, that on zkSync access lists are not supported and should always be empty. // Encode all fixed-length params to avoid "stack too deep error" bytes memory encodedFixedLengthParams; { bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedMaxPriorityFeePerGas = RLPEncoder.encodeUint256(_transaction.maxPriorityFeePerGas); bytes memory encodedMaxFeePerGas = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); encodedFixedLengthParams = bytes.concat( encodedChainId, encodedNonce, encodedMaxPriorityFeePerGas, encodedMaxFeePerGas, encodedGasLimit, encodedTo, encodedValue ); } // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // On zkSync, access lists are always zero length (at least for now). bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); bytes memory encodedListLength; unchecked { uint256 listLength = encodedFixedLengthParams.length + encodedDataLength.length + _transaction.data.length + encodedAccessListLength.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( "\x02", encodedListLength, encodedFixedLengthParams, encodedDataLength, _transaction.data, encodedAccessListLength ) ); } /// @notice Processes the common paymaster flows, e.g. setting proper allowance /// for tokens, etc. For more information on the expected behavior, check out /// the "Paymaster flows" section in the documentation. function processPaymasterInput(Transaction calldata _transaction) internal { require(_transaction.paymasterInput.length >= 4, "The standard paymaster input must be at least 4 bytes long"); bytes4 paymasterInputSelector = bytes4(_transaction.paymasterInput[0:4]); if (paymasterInputSelector == IPaymasterFlow.approvalBased.selector) { require( _transaction.paymasterInput.length >= 68, "The approvalBased paymaster input must be at least 68 bytes long" ); // While the actual data consists of address, uint256 and bytes data, // the data is needed only for the paymaster, so we ignore it here for the sake of optimization (address token, uint256 minAllowance) = abi.decode(_transaction.paymasterInput[4:68], (address, uint256)); address paymaster = address(uint160(_transaction.paymaster)); uint256 currentAllowance = IERC20(token).allowance(address(this), paymaster); if (currentAllowance < minAllowance) { // Some tokens, e.g. USDT require that the allowance is firsty set to zero // and only then updated to the new value. IERC20(token).safeApprove(paymaster, 0); IERC20(token).safeApprove(paymaster, minAllowance); } } else if (paymasterInputSelector == IPaymasterFlow.general.selector) { // Do nothing. general(bytes) paymaster flow means that the paymaster must interpret these bytes on his own. } else { revert("Unsupported paymaster flow"); } } /// @notice Pays the required fee for the transaction to the bootloader. /// @dev Currently it pays the maximum amount "_transaction.maxFeePerGas * _transaction.gasLimit", /// it will change in the future. function payToTheBootloader(Transaction calldata _transaction) internal returns (bool success) { address bootloaderAddr = BOOTLOADER_FORMAL_ADDRESS; uint256 amount = _transaction.maxFeePerGas * _transaction.gasLimit; assembly { success := call(gas(), bootloaderAddr, amount, 0, 0, 0, 0) } } // Returns the balance required to process the transaction. function totalRequiredBalance(Transaction calldata _transaction) internal pure returns (uint256 requiredBalance) { if (address(uint160(_transaction.paymaster)) != address(0)) { // Paymaster pays for the fee requiredBalance = _transaction.value; } else { // The user should have enough balance for both the fee and the value of the transaction requiredBalance = _transaction.maxFeePerGas * _transaction.gasLimit + _transaction.value; } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import "./EfficientCall.sol"; /** * @author Matter Labs * @dev Common utilities used in zkSync system contracts */ library Utils { /// @dev Bit mask of bytecode hash "isConstructor" marker bytes32 constant IS_CONSTRUCTOR_BYTECODE_HASH_BIT_MASK = 0x00ff000000000000000000000000000000000000000000000000000000000000; /// @dev Bit mask to set the "isConstructor" marker in the bytecode hash bytes32 constant SET_IS_CONSTRUCTOR_MARKER_BIT_MASK = 0x0001000000000000000000000000000000000000000000000000000000000000; function safeCastToU128(uint256 _x) internal pure returns (uint128) { require(_x <= type(uint128).max, "Overflow"); return uint128(_x); } function safeCastToU32(uint256 _x) internal pure returns (uint32) { require(_x <= type(uint32).max, "Overflow"); return uint32(_x); } function safeCastToU24(uint256 _x) internal pure returns (uint24) { require(_x <= type(uint24).max, "Overflow"); return uint24(_x); } /// @return codeLength The bytecode length in bytes function bytecodeLenInBytes(bytes32 _bytecodeHash) internal pure returns (uint256 codeLength) { codeLength = bytecodeLenInWords(_bytecodeHash) << 5; // _bytecodeHash * 32 } /// @return codeLengthInWords The bytecode length in machine words function bytecodeLenInWords(bytes32 _bytecodeHash) internal pure returns (uint256 codeLengthInWords) { unchecked { codeLengthInWords = uint256(uint8(_bytecodeHash[2])) * 256 + uint256(uint8(_bytecodeHash[3])); } } /// @notice Denotes whether bytecode hash corresponds to a contract that already constructed function isContractConstructed(bytes32 _bytecodeHash) internal pure returns (bool) { return _bytecodeHash[1] == 0x00; } /// @notice Denotes whether bytecode hash corresponds to a contract that is on constructor or has already been constructed function isContractConstructing(bytes32 _bytecodeHash) internal pure returns (bool) { return _bytecodeHash[1] == 0x01; } /// @notice Sets "isConstructor" flag to TRUE for the bytecode hash /// @param _bytecodeHash The bytecode hash for which it is needed to set the constructing flag /// @return The bytecode hash with "isConstructor" flag set to TRUE function constructingBytecodeHash(bytes32 _bytecodeHash) internal pure returns (bytes32) { // Clear the "isConstructor" marker and set it to 0x01. return constructedBytecodeHash(_bytecodeHash) | SET_IS_CONSTRUCTOR_MARKER_BIT_MASK; } /// @notice Sets "isConstructor" flag to FALSE for the bytecode hash /// @param _bytecodeHash The bytecode hash for which it is needed to set the constructing flag /// @return The bytecode hash with "isConstructor" flag set to FALSE function constructedBytecodeHash(bytes32 _bytecodeHash) internal pure returns (bytes32) { return _bytecodeHash & ~IS_CONSTRUCTOR_BYTECODE_HASH_BIT_MASK; } /// @notice Validate the bytecode format and calculate its hash. /// @param _bytecode The bytecode to hash. /// @return hashedBytecode The 32-byte hash of the bytecode. /// Note: The function reverts the execution if the bytecode has non expected format: /// - Bytecode bytes length is not a multiple of 32 /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words) /// - Bytecode words length is not odd function hashL2Bytecode(bytes calldata _bytecode) internal view returns (bytes32 hashedBytecode) { // Note that the length of the bytecode must be provided in 32-byte words. require(_bytecode.length % 32 == 0, "po"); uint256 bytecodeLenInWords = _bytecode.length / 32; require(bytecodeLenInWords < 2 ** 16, "pp"); // bytecode length must be less than 2^16 words require(bytecodeLenInWords % 2 == 1, "pr"); // bytecode length in words must be odd hashedBytecode = EfficientCall.sha(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // Setting the version of the hash hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248))); // Setting the length hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, value) ); } function safeIncreaseAllowance( IERC20 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( IERC20 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( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require( nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed" ); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall( data, "SafeERC20: low-level call failed" ); 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 Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.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 Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall( target, data, "Address: low-level delegate call failed" ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget( target, success, returndata, errorMessage ); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
{ "evmVersion": "paris", "optimizer": { "enabled": true, "mode": "3" }, "outputSelection": { "*": { "*": [ "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_perPeriodMinAmount","type":"uint256"},{"internalType":"uint8","name":"_pageLimit","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidPage","type":"error"},{"inputs":[],"name":"InvalidZeroParameter","type":"error"},{"inputs":[],"name":"LowVestingAmount","type":"error"},{"inputs":[],"name":"NoOpIsFailure","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"VestingToSelf","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"end","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"end","type":"uint256"}],"name":"Renounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"address","name":"cancelAuthority","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint32","name":"periodCount","type":"uint32"},{"internalType":"uint256","name":"perPeriodAmount","type":"uint256"}],"indexed":false,"internalType":"struct Grants.VestingSchedule","name":"schedule","type":"tuple"}],"name":"VestingScheduleAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"end","type":"uint256"}],"name":"VestingSchedulesCanceled","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint32","name":"periodCount","type":"uint32"},{"internalType":"uint256","name":"perPeriodAmount","type":"uint256"},{"internalType":"address","name":"cancelAuthority","type":"address"}],"name":"addVestingSchedule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"cancelVestingSchedules","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"currentPage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"getGrantsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pageLimit","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"perPeriodMinAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"renounce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint32","name":"periodCount","type":"uint32"},{"internalType":"uint256","name":"perPeriodAmount","type":"uint256"}],"name":"validateVestingSchedule","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingSchedules","outputs":[{"internalType":"address","name":"cancelAuthority","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint32","name":"periodCount","type":"uint32"},{"internalType":"uint256","name":"perPeriodAmount","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
9c4d535b0000000000000000000000000000000000000000000000000000000000000000010002478425b0fedf1416cdd18bb8fbba218a0d3133af31f8f50de1f465003100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000bd4372e44c5ee654dd838304006e1f0f6998315400000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000064
Deployed Bytecode
0x00030000000000020016000000000002000000000301001900000060033002700000020b033001970002000000310355000100000001035500000001022001900000002a0000c13d0000008002000039000000400020043f000000040230008c000003c00000413d000000000201043b000000e002200270000002110420009c0000005f0000a13d000002120420009c000000b10000213d000002160420009c000001e60000613d000002170420009c0000022c0000613d000002180220009c000003c00000c13d0000000002000416000000240330008c000003c00000413d000000000202004b000003c00000c13d0000000401100370000000000101043b0000020d0210009c000003c00000213d0000000000100435000000200000043f0000004001000039082807ed0000040f000000000101041a000000800010043f0000022001000041000008290001042e0000000002000416000000000202004b000003c00000c13d0000001f023000390000020c02200197000000e002200039000000400020043f0000001f0230018f00000005043002720000003d0000613d00000000050000190000000506500210000000000761034f000000000707043b000000e00660003900000000007604350000000105500039000000000645004b000000350000413d000000000502004b0000004c0000613d0000000504400210000000000141034f0000000302200210000000e004400039000000000504043300000000052501cf000000000525022f000000000101043b0000010002200089000000000121022f00000000012101cf000000000151019f0000000000140435000000600130008c000003c00000413d000000e00200043d0000020d0120009c000003c00000213d000001200100043d000000ff0310008c000003c00000213d000000000301004b000003c20000c13d000000400100043d0000020f0200004100000000002104350000020b020000410000020b0310009c0000000001028019000000400110021000000210011001c70000082a00010430000002190420009c000000c80000a13d0000021a0420009c0000023d0000613d0000021b0420009c0000024d0000613d0000021c0220009c000003c00000c13d0000000002000416000000640330008c000003c00000413d000000000202004b000003c00000c13d0000000402100370000000000202043b0000020d0320009c000003c00000213d0000004403100370000000000303043b000d00000003001d0000002401100370000000000101043b000e00000001001d00000000002004350000000101000039000000200010043f0000020b0300004100000000010004140000020b0210009c0000000001038019000000c00110021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b0000000e020000290000000000200435000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b000000000201041a0000000d0220006b000003c00000813d0000000d020000290828062a0000040f0000000302100039000000000202041a0000000103100039000000000303041a0000000204100039000000000404041a000000000501041a0000000401100039000000000101041a000000400600043d0000008007600039000000000017043500000040016000390000000000410435000000200160003900000000003104350000020b01200197000000600260003900000000001204350000020d0150019700000000001604350000020b010000410000020b0260009c000000000601801900000040016002100000022c011001c7000008290001042e000002130420009c000002620000613d000002140420009c0000037c0000613d000002150120009c000003c00000c13d0000000001000416000000000101004b000003c00000c13d0000000001000412001000000001001d000f00000000001d000080050100003900000044030000390000000004000415000000100440008a00000005044002100000021f02000041082807ff0000040f0000020d01100197000000800010043f0000022001000041000008290001042e0000021d0420009c000003a60000613d0000021e0220009c000003c00000c13d0000000002000416000000640330008c000003c00000413d000000000202004b000003c00000c13d0000000402100370000000000202043b000400000002001d0000020d0220009c000003c00000213d0000004402100370000000000202043b000300000002001d0000002401100370000000000101043b000100000001001d00000004010000290000000000100435000000200000043f0000020b0100004100000000020004140000020b0320009c0000000002018019000000c00120021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b000000000101041a000000010110003a000000000200001900000001020060390000000102200190000003a00000c13d0000000104000029000000030240006c0000043a0000213d000000000241004b0000043a0000a13d0000000303000029000000010230008a000000000212004b0000000003018019000300000003001d000000000134004b000005ab0000813d000200010000003d000580100000003d000600000004001d000b00000000001d000a00000000001d000001070000013d00000006020000290000000102200039000600000002001d000000030120006c000005da0000813d000000040100002900000000001004350000000201000029000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000000502000029082808230000040f0000000102200190000003c00000613d000000000101043b00000006020000290000000000200435000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000000502000029082808230000040f0000000102200190000003c00000613d000000000201043b000000000402041a000000000104004b000001020000613d0000000003000019000e00000002001d0000012d0000013d0000000e02000029000000000402041a0000000103300039000000000143004b000001020000813d000c00000004001d000d00000003001d000000000020043500000000010004140000020b0210009c0000020b01008041000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d0000000d0300002900000005243000c9000000000101043b0000000005410019000000000105041a0000020d011001970000000002000411000000000121004b000001280000c13d000700000004001d000900000005001d0000000101500039000000000101041a000800000001001d0000022501000041000000000010043900000000010004140000020b0210009c0000020b01008041000000c00110021000000226011001c70000800b02000039082808230000040f0000000102200190000006290000613d000000000201043b0000000804000029000000000142004b00000000010000190000000b0800002900000009060000290000015f0000a13d0000000201600039000000000101041a000000000301004b0000047c0000613d000000000242004900000000211200d90000000302600039000000000202041a0000020b05200197000000000251004b000000000405001900000000040140190000000402600039000000000302041a00000000623400a9000000000604004b0000016d0000613d00000000764200d9000000000636004b000003a00000c13d000000000645004900000000743600a9000000000115004b000001740000a13d00000000516400d9000000000131004b000003a00000c13d0000000008820019000000000128004b000000000100001900000001010040390000000101100190000003a00000c13d000a000a0040002d0000000a0140006b000000000100001900000001010040390000000101100190000003a00000c13d000b00000008001d0000000c01000029000c0001001000920000000e02000029000000000102041a0000000c0110006c000004760000a13d000000000020043500000000010004140000020b0210009c0000020b01008041000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b000900000001001d0000000e03000029000000000103041a0000000d0110006c0000000001030019000004760000a13d000000000010043500000000010004140000020b0210009c0000020b01008041000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d0000000c0200002900000005322000c90000000902200029000000000101043b0000000701100029000000000321004b000001c30000613d000000000302041a0000020d03300197000000000401041a0000022304400197000000000334019f000000000031041b0000000103200039000000000303041a0000000104100039000000000034041b0000000203200039000000000303041a0000000204100039000000000034041b0000000303200039000000000303041a0000020b033001970000000304100039000000000504041a0000022705500197000000000335019f000000000034041b00000004011000390000000402200039000000000202041a000000000021041b0000000e02000029000000000102041a000c00000001001d000000000101004b000004820000613d000000000020043500000000010004140000020b0210009c0000020b01008041000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b0000000c02000029000000010420008a00000005324000c90000000001210019000000000001041b0000000102100039000000000002041b0000000202100039000000000002041b0000000302100039000000000002041b0000000401100039000000000001041b0000000e02000029000000000042041b0000000d03000029000000000143004b0000012d0000413d000001020000013d0000000002000416000000240330008c000003c00000413d000000000202004b000003c00000c13d0000000401100370000000000101043b000b00000001001d0000020d0110009c000003c00000213d000d80100000003d000a00010000003d0000000003000019000e00000000001d000c00000003001d0000000b010000290000000000100435000000200000043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000000d02000029082808230000040f0000000102200190000003c00000613d000000000101043b000000000101041a0000000e0110006b000004440000213d0000000a01000029000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000000d02000029082808230000040f0000000102200190000003c00000613d000000000101043b0000000e020000290000000000200435000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000000d02000029082808230000040f0000000102200190000003c00000613d0000000e02000029000e00010020003e00000000020000190000000102006039000000000101043b000000000101041a0000000c03100029000000000113004b000000000100001900000001010040390000000101100190000003a00000c13d0000000101200190000001f40000613d000003a00000013d0000000001000416000000000101004b000003c00000c13d0000000001000412001200000001001d001100400000003d000080050100003900000044030000390000000004000415000000120440008a00000005044002100000021f02000041082807ff0000040f000000ff0110018f000000800010043f0000022001000041000008290001042e0000000001000416000000000101004b000003c00000c13d0000000001000412001400000001001d001300200000003d000080050100003900000044030000390000000004000415000000140440008a00000005044002100000021f02000041082807ff0000040f000000800010043f0000022001000041000008290001042e0000000002000416000000840330008c000003c00000413d000000000202004b000003c00000c13d0000000402100370000000000502043b0000020d0250009c000003c00000213d0000004402100370000000000302043b0000020b0230009c000003c00000213d0000002402100370000000000202043b0000006401100370000000000401043b0000000001050019082806470000040f0000000001000019000008290001042e0000000002000416000000440330008c000003c00000413d000000000202004b000003c00000c13d0000000402100370000000000202043b000200000002001d0000002401100370000000000101043b000500000001001d0000000001000411000400000001001d0000020d011001970000000000100435000000200000043f0000020b0100004100000000020004140000020b0320009c0000000002018019000000c00120021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b000000000101041a000000010110003a000000000200001900000001020060390000000102200190000003a00000c13d0000000203000029000000050230006c0000043a0000213d000000000231004b0000043a0000a13d0000000504000029000000010240008a000000000212004b0000000004018019000500000004001d000000000143004b000005ab0000813d000300010000003d0000020b04000041000680100000003d000700000003001d000800000000001d0000029b0000013d0000020b0400004100000007020000290000000102200039000700000002001d000000050120006c000005a90000813d000000040100002900000000001004350000000301000029000000200010043f00000000010004140000020b0210009c0000000001048019000000c00110021000000221011001c70000000602000029082808230000040f0000000102200190000003c00000613d000000000101043b00000007020000290000000000200435000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000000602000029082808230000040f0000000102200190000003c00000613d000000000c01043b00000000010c041a000000000101004b000002950000613d00000000020000190000020b04000041000c0000000c001d000002c30000013d00000000010c041a0000000e0200002900000001022000390000020b04000041000000000112004b000002960000813d000e00000002001d0000000000c0043500000000010004140000020b0210009c0000000001048019000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d0000000e0200002900090005202000cd000000000101043b0000000901100029000b00000001001d0000000101100039000a00000001001d000000000101041a000d00000001001d0000022501000041000000000010043900000000010004140000020b0210009c0000020b01008041000000c00110021000000226011001c70000800b02000039082808230000040f0000000102200190000006290000613d000000000101043b0000000d0d0000290000000002d1004b0000000c0c000029000002bd0000a13d0000000b090000290000000202900039000000000302041a000000000203004b0000047c0000613d0000000005d1004900000000213500d90000000306900039000000000706041a0000020b02700197000000000421004b000000000802001900000000080140190000000404900039000000000904041a00000000a49800a9000000000a08004b000002fc0000613d00000000ba8400d900000000099a004b000003a00000c13d00000000088200490000020b0980009c000003a00000213d0000022707700197000000000778019f000000000076041b00000000763100a9000000000553004b000003080000213d00000000751600d9000000000335004b000003a00000c13d0000000003d60019000000000563004b000000000500001900000001050040390000000105500190000003a00000c13d0000000a05000029000000000035041b000800080040002d000000080340006b000000000300001900000001030040390000000103300190000003a00000c13d000000000112004b000002bd0000213d00000000010c041a000d00000001001d000000000101004b000003a00000613d0000000000c0043500000000010004140000020b0210009c0000020b01008041000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b000b00000001001d0000000c02000029000000000102041a0000000e0110006c000004760000a13d000000000020043500000000010004140000020b0210009c0000020b01008041000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d0000000d0200002900000005322000c90000000b02200029000000050220008a000000000101043b0000000901100029000000000321004b000003580000613d000000000302041a0000020d03300197000000000401041a0000022304400197000000000334019f000000000031041b0000000103200039000000000303041a0000000104100039000000000034041b0000000203200039000000000303041a0000000204100039000000000034041b0000000303200039000000000303041a0000020b033001970000000304100039000000000504041a0000022705500197000000000335019f000000000034041b00000004011000390000000402200039000000000202041a000000000021041b0000000c02000029000000000102041a000d00000001001d000000000101004b000004820000613d000000000020043500000000010004140000020b0210009c0000020b01008041000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000201043b0000000d01000029000000010110008a00000005431000c90000000002320019000000000002041b0000000103200039000000000003041b0000000203200039000000000003041b0000000303200039000000000003041b0000000402200039000000000002041b0000000c0c00002900000000001c041b0000020b040000410000000e02000029000000000112004b000002c30000413d000002960000013d0000000002000416000000640330008c000003c00000413d000000000202004b000003c00000c13d0000000402100370000000000202043b000800000002001d0000020d0220009c000003c00000213d0000004402100370000000000202043b000700000002001d0000002401100370000000000101043b000600000001001d00000008010000290000000000100435000000200000043f0000020b0100004100000000020004140000020b0320009c0000000002018019000000c00120021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b000000000101041a000000010110003a000000000200001900000001020060390000000102200190000003d30000613d0000022d0100004100000000001004350000001101000039000000040010043f0000022e010000410000082a000104300000000002000416000000c40330008c000003c00000413d000000000202004b000003c00000c13d0000000402100370000000000202043b000e00000002001d0000020d0220009c000003c00000213d0000004402100370000000000202043b000d00000002001d0000006402100370000000000202043b000c020b0020019b0000020b0220009c000003c00000213d0000008402100370000000000202043b000b00000002001d000000a401100370000000000101043b000a020d0010019b0000020d0110009c0000043d0000a13d00000000010000190000082a00010430000001000300043d000000800020043f000000a00030043f000000c00010043f0000014000000443000001600020044300000020020000390000018000200443000001a0003004430000004003000039000001c000300443000001e0001004430000010000200443000000030100003900000120001004430000020e01000041000008290001042e0000000604000029000000070240006c0000043a0000213d000000000241004b0000043a0000a13d0000000703000029000000010230008a000000000212004b0000000003018019000700000003001d000000000134004b000005ab0000813d000b00010000003d000980100000003d000a00000004001d000c00000000001d000003e90000013d0000000a020000290000000102200039000a00000002001d000000070120006c000005910000813d000000080100002900000000001004350000000b01000029000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000000902000029082808230000040f0000000102200190000003c00000613d000000000101043b0000000a020000290000000000200435000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000000902000029082808230000040f0000000102200190000003c00000613d000000000201043b000000000102041a000000000101004b000003e40000613d0000000003000019000d00000002001d0000040f0000013d00000001033000390000000d02000029000000000102041a000000000113004b000003e40000813d000e00000003001d000000000020043500000000010004140000020b0210009c0000020b01008041000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d0000000e0300002900000005243000c9000000000101043b0000000001410019000000000101041a0000020d011001970000000002000411000000000121004b0000040a0000c13d000c00000004001d0000000d02000029000000000102041a000000000131004b000004760000a13d000000000020043500000000010004140000020b0210009c0000020b01008041000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b0000000c01100029000000000201041a0000022302200197000000000021041b000c000b0000002d0000000e030000290000040a0000013d000000400100043d0000023102000041000000580000013d0000000e03000029000000000103004b0000044d0000c13d0000020f01000041000000800010043f0000023a010000410000082a00010430000000400100043d0000000c0200002900000000002104350000020b020000410000020b0310009c000000000102801900000040011002100000022b011001c7000008290001042e0000000002000411000000000132004b000004540000c13d0000023901000041000000800010043f0000023a010000410000082a000104300000000d0100006b000004400000613d0000000c0100006b000004400000613d000800000002001d0000021f0100004100000000001004390000000001000412000900000001001d0000000400100443000000200100003900000024001004430000020b0100004100000000020004140000020b0320009c0000000002018019000000c00120021000000228011001c70000800502000039082808230000040f0000000102200190000006290000613d000000400300043d000000000101043b0000000b0110006c000004880000a13d000002380100004100000000001304350000020b010000410000020b0230009c0000000003018019000000400130021000000210011001c70000082a000104300000022d0100004100000000001004350000003201000039000000040010043f0000022e010000410000082a000104300000022d0100004100000000001004350000001201000039000000040010043f0000022e010000410000082a000104300000022d0100004100000000001004350000003101000039000000040010043f0000022e010000410000082a00010430000700000003001d000002320130009c000004910000413d0000022d0100004100000000001004350000004101000039000000040010043f0000022e010000410000082a000104300000000705000029000000a001500039000000400010043f0000000a01000029000000000415043600000024010000390000000101100367000000000101043b00000080035000390000000b02000029000a00000003001d000000000023043500000060035000390000000c02000029000600000003001d00000000002304350000004002500039000500000002001d0000000d030000290000000000320435000d00000004001d00000000001404350000000e010000290000000000100435000000200000043f0000020b0300004100000000010004140000020b0210009c0000000001038019000000c00110021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b000000000101041a000400000001001d0000000101000039000300000001001d000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b00000004020000290000000000200435000000200010043f0000020b0300004100000000010004140000020b0210009c0000000001038019000000c00110021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b000000000101041a000200000001001d0000021f010000410000000000100439000000090100002900000004001004430000004001000039000000240010044300000000010004140000020b0210009c0000020b01008041000000c00110021000000228011001c70000800502000039082808230000040f0000000102200190000006290000613d000000000101043b000000ff0110018f000000020110006b000004fd0000c13d0000000401000029000400010010003e000000000100001900000001010060390000000101100190000003a00000c13d0000000e010000290000000000100435000000200000043f0000020b0100004100000000020004140000020b0320009c0000000002018019000000c00120021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b0000000402000029000000000021041b0000000e0100002900000000001004350000000301000029000000200010043f0000020b0300004100000000010004140000020b0210009c0000000001038019000000c00110021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b00000004020000290000000000200435000000200010043f00000000010004140000020b0210009c0000020b01008041000000c00110021000000221011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000000101043b000000000201041a000400000002001d000002330220009c0000048b0000213d00000004020000290000000102200039000000000021041b00000000001004350000020b0300004100000000010004140000020b0210009c0000000001038019000000c00110021000000222011001c70000801002000039082808230000040f0000000102200190000003c00000613d000000040200002900000005322000c9000000000101043b0000000001210019000000000201041a0000022302200197000000070300002900000000030304330000020d03300197000000000232019f000000000021041b00000001021000390000000d060000290000000004060433000000000042041b000000020210003900000005080000290000000004080433000000000042041b0000000302100039000000000402041a0000022704400197000000060700002900000000050704330000020b05500197000000000454019f000000000042041b00000004011000390000000a040000290000000002040433000000000021041b000000400100043d00000000023104360000000003060433000000000032043500000000020804330000004003100039000000000023043500000000020704330000020b02200197000000600310003900000000002304350000000002040433000000800310003900000000002304350000020b0210009c0000020b04000041000000000104801900000000020004140000020b0320009c00000000020480190000004001100210000000c002200210000000000112019f00000234011001c70000800d02000039000000020300003900000235040000410000000e050000290828081e0000040f0000000101200190000003c00000613d0000000b030000290000000c213000b9000000000203004b000005700000613d0000000b321000fa0000000c0220006c000003a00000c13d000000400400043d00000020024000390000023603000041000000000032043500000064024000390000000000120435000000440140003900000000020004100000000000210435000000240140003900000008020000290000000000210435000000640100003900000000001404350000000002040019000002370140009c0000048b0000213d000e00000002001d000000a001200039000000400010043f001600090000002d001500000000001d000080050100003900000044030000390000000004000415000000160440008a00000005044002100000021f02000041082807ff0000040f0000000e02000029082807390000040f0000000001000019000008290001042e0000000c0100006b0000000604000029000005ab0000613d000000400100043d00000020021000390000000703000029000000000032043500000000004104350000020b0200004100000000030004140000020b0430009c00000000030280190000020b0410009c00000000010280190000004001100210000000c002300210000000000112019f00000221011001c70000800d020000390000000303000039000000000500041100000224040000410000000806000029000006240000013d000000080100006b000005ae0000c13d000000400100043d0000023002000041000000580000013d0000021f0100004100000000001004390000000001000412000000040010044300000024000004430000020b0300004100000000010004140000020b0210009c0000000001038019000000c00110021000000228011001c70000800502000039082808230000040f0000000102200190000006290000613d000000000101043b00000004020000290000000803000029000800000003001d082806770000040f000000400100043d0000004002100039000000050300002900000000003204350000002002100039000000020300002900000000003204350000000802000029000000000021043500000000020004140000020b0320009c0000020b0400004100000000020480190000020b0310009c00000000010480190000004001100210000000c002200210000000000112019f00000229011001c70000800d0200003900000002030000390000022a040000410000000405000029000006240000013d0000000b0100006b000005e10000c13d0000000a0100006b000005ab0000613d0000000001000412000e00000001001d000005fa0000013d0000021f0100004100000000001004390000000001000412000e00000001001d000000040010044300000024000004430000020b0100004100000000020004140000020b0320009c0000000002018019000000c00120021000000228011001c70000800502000039082808230000040f0000000102200190000006290000613d000000000101043b00000004020000290000000b03000029082806770000040f0000000a0100006b000005fa0000c13d0000000001000411000e00000001001d0000060e0000013d0000021f0100004100000000001004390000000e01000029000000040010044300000024000004430000020b0100004100000000020004140000020b0320009c0000000002018019000000c00120021000000228011001c70000800502000039082808230000040f0000000102200190000006290000613d000000000101043b0000000002000411000e00000002001d0000000a03000029082806770000040f000000400100043d000000200210003900000003030000290000000000320435000000010200002900000000002104350000020b0200004100000000030004140000020b0430009c00000000030280190000020b0410009c00000000010280190000004001100210000000c002300210000000000112019f00000221011001c700000004020000290000020d062001970000800d0200003900000003030000390000022f040000410000000e050000290828081e0000040f0000000101200190000003c00000613d0000000001000019000008290001042e000000000001042f0001000000000002000000000301041a000100000002001d000000000223004b0000063f0000a13d00000000001004350000020b0100004100000000020004140000020b0320009c0000000002018019000000c00120021000000222011001c70000801002000039082808230000040f0000000102200190000006450000613d000000010200002900000005322000c9000000000101043b0000000001210019000000000001042d0000022d0100004100000000001004350000003201000039000000040010043f0000022e010000410000082a0001043000000000010000190000082a0001043000010000000000020000020d01100198000006670000613d0000000005000411000000000115004b0000066a0000613d0000020b01300197000000000202004b000006670000613d000000000101004b000006670000613d000100000004001d0000021f01000041000000000010043900000000010004120000000400100443000000200100003900000024001004430000020b0100004100000000020004140000020b0320009c0000000002018019000000c00120021000000228011001c70000800502000039082808230000040f00000001022001900000066d0000613d000000000101043b000000010110006c0000066e0000213d000000000001042d000000400100043d0000020f02000041000006700000013d000000400100043d0000023902000041000006700000013d000000000001042f000000400100043d000002380200004100000000002104350000020b020000410000020b0310009c0000000001028019000000400110021000000210011001c70000082a000104300003000000000002000000400400043d0000004405400039000000000035043500000020034000390000023b0500004100000000005304350000020d0220019700000024054000390000000000250435000000440200003900000000002404350000023c0240009c0000070a0000813d0000008002400039000000400020043f000000000404043300000000020004140000020d08100197000000040180008c000006bf0000c13d00000001020000390000000001000031000000000301004b000006d60000613d000002330310009c0000070a0000213d0000001f03100039000000200400008a000000000343016f0000003f03300039000000000343016f000000400a00043d00000000033a00190000000004a3004b00000000040000190000000104004039000002330530009c0000070a0000213d00000001044001900000070a0000c13d000000400030043f0000001f0310018f00000000091a043600000002040003670000000501100272000006af0000613d000000000500001900000005065002100000000007690019000000000664034f000000000606043b00000000006704350000000105500039000000000615004b000006a70000413d000000000503004b000006d80000613d0000000501100210000000000414034f00000000011900190000000303300210000000000501043300000000053501cf000000000535022f000000000404043b0000010003300089000000000434022f00000000033401cf000000000353019f0000000000310435000006d80000013d0000020b010000410000020b0530009c000000000301801900000040033002100000020b0540009c00000000040180190000006004400210000000000334019f0000020b0420009c0000000002018019000000c001200210000000000113019f0000000002080019000300000008001d0828081e0000040f0000000308000029000000010220018f000200000001035500000060011002700000020b0010019d0000020b01100197000000000301004b000006900000c13d000000600a000039000000800900003900000000010a0433000000000202004b000007120000613d000000000201004b000006f60000c13d00020000000a001d000100000009001d0000023d010000410000000000100439000300000008001d00000004008004430000020b0100004100000000020004140000020b0320009c0000000002018019000000c0012002100000023e011001c70000800202000039082808230000040f00000001022001900000072c0000613d000000000101043b000000000101004b00000002010000290000072d0000613d0000000001010433000000000201004b00000003080000290000000109000029000007090000613d0000023f02000041000000200310008c000000000300001900000000030240190000023f01100197000000000401004b000000000200a0190000023f0110009c000000000203c019000000000102004b000007100000c13d0000000001090433000000000201004b0000000002000019000000010200c039000000000221004b000007100000c13d000000000101004b0000071d0000613d000000000001042d0000022d0100004100000000001004350000004101000039000000040010043f0000022e010000410000082a0001043000000000010000190000082a00010430000000000201004b000007230000c13d000000400100043d000002420200004100000000002104350000020b020000410000020b0310009c0000000001028019000000400110021000000210011001c70000082a00010430000000400100043d0000024002000041000000000021043500000004021000390000000000820435000007330000013d0000020b020000410000020b0310009c00000000010280190000020b0390009c000000000902801900000040029002100000006001100210000000000121019f0000082a00010430000000000001042f000000400100043d000002410200004100000000002104350000000402100039000000030300002900000000003204350000020b020000410000020b0310009c000000000102801900000040011002100000022e011001c70000082a000104300003000000000002000000003402043400000000020004140000020d08100197000000040180008c000007720000c13d00000001020000390000000001000031000000000301004b000007890000613d000002430310009c000007bf0000813d0000001f03100039000000200400008a000000000343016f0000003f03300039000000000343016f000000400a00043d00000000033a00190000000004a3004b00000000040000190000000104004039000002330530009c000007bf0000213d0000000104400190000007bf0000c13d000000400030043f0000001f0310018f00000000091a043600000002040003670000000501100272000007620000613d000000000500001900000005065002100000000007690019000000000664034f000000000606043b00000000006704350000000105500039000000000615004b0000075a0000413d000000000503004b0000078b0000613d0000000501100210000000000414034f00000000011900190000000303300210000000000501043300000000053501cf000000000535022f000000000404043b0000010003300089000000000434022f00000000033401cf000000000353019f00000000003104350000078b0000013d0000020b010000410000020b0540009c000000000401801900000060044002100000020b0530009c00000000030180190000004003300210000000000334019f0000020b0420009c0000000002018019000000c001200210000000000113019f0000000002080019000300000008001d0828081e0000040f0000000308000029000000010220018f000200000001035500000060011002700000020b0010019d0000020b01100197000000000301004b000007430000c13d000000600a000039000000800900003900000000010a0433000000000202004b000007c50000613d000000000201004b000007a90000c13d00020000000a001d000100000009001d0000023d010000410000000000100439000300000008001d00000004008004430000020b0100004100000000020004140000020b0320009c0000000002018019000000c0012002100000023e011001c70000800202000039082808230000040f0000000102200190000007df0000613d000000000101043b000000000101004b0000000201000029000007e00000613d0000000001010433000000000201004b00000003080000290000000109000029000007bc0000613d0000023f02000041000000200310008c000000000300001900000000030240190000023f01100197000000000401004b000000000200a0190000023f0110009c000000000203c019000000000102004b000007bd0000c13d0000000001090433000000000201004b0000000002000019000000010200c039000000000221004b000007bd0000c13d000000000101004b000007d00000613d000000000001042d00000000010000190000082a000104300000022d0100004100000000001004350000004101000039000000040010043f0000022e010000410000082a00010430000000000201004b000007d60000c13d000000400100043d000002420200004100000000002104350000020b020000410000020b0310009c0000000001028019000000400110021000000210011001c70000082a00010430000000400100043d0000024002000041000000000021043500000004021000390000000000820435000007e60000013d0000020b020000410000020b0310009c00000000010280190000020b0390009c000000000902801900000040029002100000006001100210000000000121019f0000082a00010430000000000001042f000000400100043d000002410200004100000000002104350000000402100039000000030300002900000000003204350000020b020000410000020b0310009c000000000102801900000040011002100000022e011001c70000082a00010430000000000001042f0000020b0200004100000000030004140000020b0430009c00000000030280190000020b0410009c00000000010280190000006001100210000000c002300210000000000112019f00000244011001c70000801002000039082808230000040f0000000102200190000007fd0000613d000000000101043b000000000001042d00000000010000190000082a0001043000000000050100190000000000200439000000050130008c0000080d0000413d000000040100003900000000020000190000000506200210000000000664001900000005066002700000000006060031000000000161043a0000000102200039000000000631004b000008050000413d0000020b0100004100000000020004140000020b0420009c00000000020180190000020b0430009c00000000030180190000006001300210000000c002200210000000000112019f00000245011001c70000000002050019082808230000040f00000001022001900000081d0000613d000000000101043b000000000001042d000000000001042f00000821002104210000000102000039000000000001042d0000000002000019000000000001042d00000826002104230000000102000039000000000001042d0000000002000019000000000001042d0000082800000432000008290001042e0000082a00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000001ffffffe0000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000200000000000000000000000000000100000001000000000000000000786ec51f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000511fdbf200000000000000000000000000000000000000000000000000000000c349026200000000000000000000000000000000000000000000000000000000c349026300000000000000000000000000000000000000000000000000000000d75dfb2e00000000000000000000000000000000000000000000000000000000fc0c546a00000000000000000000000000000000000000000000000000000000511fdbf3000000000000000000000000000000000000000000000000000000009786f96e00000000000000000000000000000000000000000000000000000000bb9bb4b20000000000000000000000000000000000000000000000000000000017fe20f80000000000000000000000000000000000000000000000000000000017fe20f90000000000000000000000000000000000000000000000000000000032f4812e0000000000000000000000000000000000000000000000000000000045e5441f000000000000000000000000000000000000000000000000000000000315a8e70000000000000000000000000000000000000000000000000000000006ecf036310ab089e4439a4c15d089f94afb7896ff553aecb10793d0ab882de59d99a32e000000000000000000000000000000000000002000000080000000000000000002000000000000000000000000000000000000400000000000000000000000000200000000000000000000000000000000000020000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000009e30bf24932320be07be594b1380877480dd36c850917956bdc2d01be9845c02796b89b91644bc98cd93958e4c9038275d622183e25ac5af08cc6b5d955391320200000200000000000000000000000000000004000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000020000020000000000000000000000000000004400000000000000000000000002000000000000000000000000000000000000600000000000000000000000009cdcf2f7714cca3508c7f0110b04a90a80a3a8dd0e35de99689db74d28c5383e000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000004e487b71000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000520c622ff71e48f0a8ac9173d2ff98c8cf6b4e40666dd18232168bc027ca3ee4aa744c31000000000000000000000000000000000000000000000000000000009ee3199600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff60000000000000000000000000000000000000000000000000ffffffffffffffff02000000000000000000000000000000000000a00000000000000000000000008ebf2d98b84ee968339a3bedaa75744e370f0f7e45c8eaefeb69fda345cbc14b23b872dd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff5fe00fed88000000000000000000000000000000000000000000000000000000000c4e36d3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000800000000000000000a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff801806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b83020000020000000000000000000000000000002400000000000000000000000080000000000000000000000000000000000000000000000000000000000000005274afe7000000000000000000000000000000000000000000000000000000009996b315000000000000000000000000000000000000000000000000000000001425ea4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000002000000000000000000000000000000000000000000000000000000000000000200000200000000000000000000000000000000000000000000000000000000df19831f370fb1ae335ead2cdadc30fd2bee88d244d02c1cf5db126006ba8a98
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000bd4372e44c5ee654dd838304006e1f0f6998315400000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000064
-----Decoded View---------------
Arg [0] : _token (address): 0xBD4372e44c5eE654dd838304006E1f0f69983154
Arg [1] : _perPeriodMinAmount (uint256): 1000000000000000000000
Arg [2] : _pageLimit (uint8): 100
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000bd4372e44c5ee654dd838304006e1f0f69983154
Arg [1] : 00000000000000000000000000000000000000000000003635c9adc5dea00000
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000064
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ZKSYNC | 100.00% | $0.002045 | 2,744,280,186.0494 | $5,612,052.98 |
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.